Nestjs教程使用JWT进行无状态认证

在使用NestJS进行JWT无状态认证时遇到几个问题:

  1. 如何在NestJS中正确配置JWT模块?官方文档的示例感觉不够详细
  2. 生成Token时应该包含哪些标准字段?除了sub和exp还需要加什么?
  3. 前端收到Token后应该存在哪里?LocalStorage和Cookie哪种方式更安全?
  4. 如何优雅地处理Token过期情况?目前只会强制跳转到登录页
  5. 刷新Token的机制应该如何实现?看到有说用双Token方案的但不太理解具体流程
  6. 为什么我的守卫有时候会跳过验证?是不是和路由顺序有关?
  7. 在生产环境部署时需要注意哪些JWT相关的安全事项?

3 回复

在NestJS中使用JWT实现无状态认证是一种常见的方式。以下是基本步骤:

  1. 安装必要的依赖:
npm install @nestjs/jwt @nestjs/passport passport passport-jwt jsonwebtoken
  1. 创建一个AuthModule,在该模块中配置JWT策略:
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secret: 'your-secret-key', // 设置密钥
      signOptions: { expiresIn: '60m' }, // 设置过期时间
    }),
  ],
})
export class AuthModule {}
  1. 创建一个JWT策略文件,例如 jwt.strategy.ts:
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'your-secret-key',
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}
  1. 在控制器中实现登录和验证逻辑,生成JWT令牌并返回给客户端。

通过这种方式,你可以实现基于JWT的无状态认证,客户端每次请求时只需携带JWT令牌即可完成身份验证。


  1. 安装依赖:首先安装 [@nestjs](/user/nestjs)/jwt[@nestjs](/user/nestjs)/passport 等相关包。

    npm install [@nestjs](/user/nestjs)/jwt [@nestjs](/user/nestjs)/passport passport passport-jwt --save
    
  2. 配置Passport:创建一个服务用于验证 JWT,例如 auth.service.ts

    import { Injectable } from '[@nestjs](/user/nestjs)/common';
    import * as jwt from 'jsonwebtoken';
    
    [@Injectable](/user/Injectable)()
    export class AuthService {
        private readonly jwtSecret = 'your_jwt_secret';
    
        async signToken(payload: any): Promise<string> {
            return jwt.sign(payload, this.jwtSecret, { expiresIn: '1h' });
        }
    
        async validateUser(payload: any): Promise<any> {
            // 验证用户逻辑
            return payload;
        }
    }
    
  3. 创建策略:创建一个 JWT 策略文件 jwt.strategy.ts

    import { Injectable } from '[@nestjs](/user/nestjs)/common';
    import { PassportStrategy } from '[@nestjs](/user/nestjs)/passport';
    import { ExtractJwt, Strategy } from 'passport-jwt';
    
    [@Injectable](/user/Injectable)()
    export class JwtStrategy extends PassportStrategy(Strategy) {
        constructor() {
            super({
                jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
                ignoreExpiration: false,
                secretOrKey: 'your_jwt_secret',
            });
        }
    
        async validate(payload: any) {
            return { userId: payload.sub, username: payload.username };
        }
    }
    
  4. 设置路由和控制器:在控制器中使用 JWT 进行登录和验证。

    import { Controller, Post, Body, Request, UseGuards } from '[@nestjs](/user/nestjs)/common';
    import { AuthService } from './auth.service';
    import { AuthGuard } from '[@nestjs](/user/nestjs)/passport';
    
    [@Controller](/user/Controller)('auth')
    export class AuthController {
        constructor(private authService: AuthService) {}
    
        @Post('login')
        async login(@Body() body: any) {
            const user = await this.authService.validateUser(body);
            if (!user) throw new Error('Invalid credentials');
            const token = await this.authService.signToken(user);
            return { access_token: token };
        }
    
        @Post('protected')
        @UseGuards(AuthGuard('jwt'))
        getProfile(@Request() req) {
            return req.user;
        }
    }
    
  5. 测试:通过登录接口获取 token,并在请求头中携带 Authorization: Bearer <token> 测试受保护的接口。

NestJS中使用JWT进行无状态认证

基本配置

  1. 首先安装必要的依赖包:
npm install @nestjs/jwt passport passport-jwt
npm install @types/passport-jwt --save-dev
  1. 创建JWT模块和策略:
// auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secret: 'your-secret-key', // 在实际应用中应使用环境变量
      signOptions: { expiresIn: '1h' },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService, JwtStrategy],
})
export class AuthModule {}

JWT策略实现

// jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'your-secret-key',
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

认证服务

// auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

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

  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

保护路由

// your.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller('profile')
export class ProfileController {
  @UseGuards(AuthGuard('jwt'))
  @Get()
  getProfile() {
    return { message: 'Protected route' };
  }
}

注意事项

  1. 在实际应用中,应该使用环境变量来存储JWT密钥
  2. 适当设置JWT过期时间
  3. 考虑使用Refresh Token机制
  4. 在生产环境中应使用HTTPS传输JWT

以上是NestJS中使用JWT进行无状态认证的基本实现。根据实际需求可以进一步扩展和完善。

回到顶部