我正在尝试使用两种AuthInterceptor
方法ErrorInterceptor
和Angular HttpInterceptor
刷新JWT令牌过期后的方法。我通过AuthInterceptor
请求的标头发送令牌:
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const Token = this.authService.getToken(); // localStorage.getItem('token', 'provider', ...)
if (Token) {
// if token exists (logged), send custom header values with request
const authReqRepeat = req.clone({
headers: req.headers
.set('Authorization', 'Bearer ' + Token.token)
.set('X-Auth-Provider', Token.provider)
});
return next.handle(authReqRepeat);
} else {
return next.handle(req);
}
}
}
RefreshToken
方法在ErrorInterceptor
中被调用,它将生成一个新的令牌对象,该对象将在失败的请求中使用(因为令牌已过期):
export class ErrorInterceptor implements HttpInterceptor {
constructor(private alertService: AlertService, private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(catchError((err: HttpErrorResponse) => {
if (err.status === 401) {
this.authService.logout();
} else if (err.status === 403) {
// Token expired error, so I need to generate a new token
const Token = this.authService.getToken();
return this.authService
.refreshToken(Token.refreshToken)
.pipe(flatMap((newToken) => {
// store new access token on local storage
localStorage.setItem('token', newToken.token);
localStorage.setItem('expiration', newToken.expiresIn);
// retry request with new access token generated
return next.handle(req.clone({
headers: req.headers
.set('Authorization', 'Bearer ' + newToken.token)
.set('X-Auth-Provider', Token.provider)
}));
}));
} else {
if (err.error.message) {
this.alertService.error(err.error.message);
} else {
this.alertService.error('Não foi possível completar a requisição.');
}
return throwError(err);
}
}));
}
}
在HttpBackEnd
调用新请求之后,我不得不使用AuthInterceptor
来停止无限循环通过HttpErrorInterceptor
:
export class AuthService {
constructor (private http: HttpClient, private handler: HttpBackend) {}
refreshToken(token: string) {
const http = new HttpClient(this.handler);
return http.post<{token: string, expiresIn: string}>(`${BACKEND_URL}/refreshtoken`, {token: token});
}
}
Node.js请求:
//routes
router.post('/refreshtoken', auth, user.refreshToken);
// controllers
module.exports = async(req, res, next) => {
try {
if (req.headers['x-auth-provider'] === 'jwt') {
// refresh token
const token = req.headers.authorization.split(' ')[1];
const decodedToken = await jwt.verify(token, process.env.JWT_Key);
req.userData = {id: decodedToken.userId, email: decodedToken.email, role: decodedToken.role};
}
next();
} catch (error) {
if (error.name && error.name === 'TokenExpiredError') {
res.status(403).json({success: false, message: 'Token inválido.'});
}
res.status(401).json({success: false, message: 'Autenticação falhou.'});
}
};
exports.refreshToken = wrap(async(req, res, next) => {
const user = await User.findOne({ refresh_token: req.body.token });
if (user) {
const newToken = await jwt.sign(
{ email: user.email, userId: user._id, role: user.role },
process.env.JWT_Key,
{ expiresIn: '15m' });
const expiresAt = moment().add(900, 'second');
res.status(200).json({success: true, token: newToken, expiresIn: JSON.stringify(expiresAt.valueOf())});
} else {
res.status(401).json({success: false, message: 'Autenticação falhou.'});
}
});
它似乎可以正常工作,但是由于我的Node.js服务器抛出了一些错误:UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
。
我在做什么错了?