简体   繁体   English

如何在 Nestjs 中刷新令牌

[英]How to refresh token in Nestjs

import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtPayload } from './model/jwt-payload.model';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'secretKey',
    });
  }

  async validate(payload: JwtPayload) {
    const user = await this.authService.validateUser(payload);
    if (!user) {
      throw new UnauthorizedException();
    }
    return true;
  }
}

Token is extracted from the request by PassportStrategy .令牌由PassportStrategy从请求中提取。 I don't know how to catch the error when the token expires or gets invalid.我不知道如何在令牌过期或无效时捕获错误。 My purpose is if there is an error because the token expired, I need to refresh the token.我的目的是如果因为令牌过期而出现错误,我需要刷新令牌。 Otherwise do something else.否则做别的事情。

Refresh token implementation could be handled in canActivate method in custom auth guard.可以在自定义身份验证防护中的canActivate方法中处理刷新令牌实现。

If the access token is expired, the refresh token will be used to obtain a new access token.如果访问令牌过期,刷新令牌将用于获取新的访问令牌。 In that process, refresh token is updated too.在该过程中,刷新令牌也会更新。

If both tokens aren't valid, cookies will be cleared.如果两个令牌都无效,则 cookie 将被清除。

@Injectable()
export class CustomAuthGuard extends AuthGuard('jwt') {
  private logger = new Logger(CustomAuthGuard.name);

  constructor(
    private readonly authService: AuthService,
    private readonly userService: UserService,
  ) {
    super();
  }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();

    try {
      const accessToken = ExtractJwt.fromExtractors([cookieExtractor])(request);
      if (!accessToken)
        throw new UnauthorizedException('Access token is not set');

      const isValidAccessToken = this.authService.validateToken(accessToken);
      if (isValidAccessToken) return this.activate(context);

      const refreshToken = request.cookies[REFRESH_TOKEN_COOKIE_NAME];
      if (!refreshToken)
        throw new UnauthorizedException('Refresh token is not set');
      const isValidRefreshToken = this.authService.validateToken(refreshToken);
      if (!isValidRefreshToken)
        throw new UnauthorizedException('Refresh token is not valid');

      const user = await this.userService.getByRefreshToken(refreshToken);
      const {
        accessToken: newAccessToken,
        refreshToken: newRefreshToken,
      } = this.authService.createTokens(user.id);

      await this.userService.updateRefreshToken(user.id, newRefreshToken);

      request.cookies[ACCESS_TOKEN_COOKIE_NAME] = newAccessToken;
      request.cookies[REFRESH_TOKEN_COOKIE_NAME] = newRefreshToken;

      response.cookie(ACCESS_TOKEN_COOKIE_NAME, newAccessToken, COOKIE_OPTIONS);
      response.cookie(
        REFRESH_TOKEN_COOKIE_NAME,
        newRefreshToken,
        COOKIE_OPTIONS,
      );

      return this.activate(context);
    } catch (err) {
      this.logger.error(err.message);
      response.clearCookie(ACCESS_TOKEN_COOKIE_NAME, COOKIE_OPTIONS);
      response.clearCookie(REFRESH_TOKEN_COOKIE_NAME, COOKIE_OPTIONS);
      return false;
    }
  }

  async activate(context: ExecutionContext): Promise<boolean> {
    return super.canActivate(context) as Promise<boolean>;
  }

  handleRequest(err, user) {
    if (err || !user) {
      throw new UnauthorizedException();
    }

    return user;
  }
}

Attaching user to the request is done in validate method in JwtStrategy class, it will be called if the access token is valid将用户附加到请求是在JwtStrategy类中的validate方法中JwtStrategy ,如果访问令牌有效,它将被调用

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    readonly configService: ConfigService,
    private readonly userService: UserService,
  ) {
    super({
      jwtFromRequest: cookieExtractor,
      ignoreExpiration: false,
      secretOrKey: configService.get('jwt.secret'),
    });
  }

  async validate({ id }): Promise<User> {
    const user = await this.userService.get(id);
    if (!user) {
      throw new UnauthorizedException();
    }

    return user;
  }
}

Instead of using the built-in AuthGuard you can create your own one and overwrite the request handler:您可以创建自己的AuthGuard并覆盖请求处理程序,而不是使用内置的AuthGuard

@Injectable()
export class MyAuthGuard extends AuthGuard('jwt') {

  handleRequest(err, user, info: Error) {
    if (info instanceof TokenExpiredError) {
      // do stuff when token is expired
      console.log('token expired');
    }
    return user;
  }

}

Depending on what you want to do, you can also overwrite the canActivate method where you have access to the request object.根据您想要做什么,您还可以覆盖可以访问请求对象的canActivate方法。 Have a look at the AuthGuard sourcecode .查看AuthGuard源代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM