如何使用Angular Http拦截器刷新JWT令牌?

时间:2018-10-04 18:00:48

标签: javascript node.js angular jwt angular-http-interceptors

我正在尝试使用两种AuthInterceptor方法ErrorInterceptorAngular 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

我在做什么错了?

0 个答案:

没有答案