You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJS中处理401状态码时自动刷新JWT并重试请求

在NestJS中实现JWT自动刷新与请求重试

核心逻辑

可以通过NestJS的**拦截器(Interceptor)**捕获受保护路由返回的401错误,自动调用刷新令牌接口获取新的访问令牌,再重新发起原请求。整个流程无需前端介入,完全在服务端处理。

实现步骤

1. 编写刷新令牌服务

先实现一个负责验证刷新令牌、生成新访问令牌的服务:

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  async refreshAccessToken(refreshToken: string): Promise<{ accessToken: string }> {
    // 验证刷新令牌合法性(可结合Redis检查是否被拉黑/过期)
    const payload = this.jwtService.verify(refreshToken, { secret: process.env.JWT_REFRESH_SECRET });
    
    // 生成新的短期访问令牌
    const newAccessToken = this.jwtService.sign(
      { userId: payload.userId },
      { expiresIn: '15m', secret: process.env.JWT_ACCESS_SECRET }
    );
    
    return { accessToken: newAccessToken };
  }
}

2. 实现自动刷新拦截器

创建拦截器捕获401响应,执行刷新逻辑并重试请求:

@Injectable()
export class TokenRefreshInterceptor implements NestInterceptor {
  constructor(private readonly authService: AuthService) {}

  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
    const request = context.switchToHttp().getRequest();
    const response = context.switchToHttp().getResponse();
    
    return next.handle().pipe(
      catchError(async (error) => {
        // 仅处理携带刷新令牌的401请求
        if (error.status === 401 && request.cookies?.refreshToken) {
          try {
            // 获取新的访问令牌
            const { accessToken } = await this.authService.refreshAccessToken(request.cookies.refreshToken);
            
            // 更新请求头的Authorization字段
            request.headers.authorization = `Bearer ${accessToken}`;
            
            // 将新令牌写入Cookie(也可返回给前端)
            response.cookie('accessToken', accessToken, { 
              httpOnly: true, 
              secure: process.env.NODE_ENV === 'production',
              maxAge: 15 * 60 * 1000 // 与访问令牌有效期一致
            });
            
            // 重新发起原请求
            const httpServer = context.switchToHttp().getHttpServer();
            return httpServer.handle(context);
          } catch (refreshError) {
            // 刷新失败,直接返回401要求重新登录
            throw new UnauthorizedException('Refresh token invalid');
          }
        }
        // 非目标错误,直接抛出原异常
        throw error;
      })
    );
  }
}

3. 配置拦截器与守卫的执行顺序

NestJS中守卫(如JwtAuthGuard)会先于拦截器执行,所以要确保拦截器能捕获守卫抛出的401错误。可以全局注册拦截器:

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: TokenRefreshInterceptor,
    },
  ],
})
export class AppModule {}

也可以在特定受保护路由上单独使用:

@Controller('user')
@UseInterceptors(TokenRefreshInterceptor)
export class UserController {
  @Get('profile')
  @UseGuards(JwtAuthGuard)
  getProfile(@Request() req) {
    return req.user;
  }
}

4. 关键注意事项

  • 避免重复刷新:可以用Redis或内存缓存记录正在刷新的用户ID,防止同一用户并发触发多次刷新请求。
  • 重试边界控制:只重试一次,避免因刷新逻辑异常导致死循环。
  • 安全性保障:刷新令牌必须存储在HttpOnly+Secure的Cookie中,防止XSS攻击;定期轮换刷新令牌,降低泄露风险。
  • 过期处理:若刷新令牌已过期,直接返回401,引导用户重新登录。

内容的提问来源于stack exchange,提问作者Arshia Arsh1a Faraji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:17:24