Nestjs项目实战 JWT认证机制的实现

在NestJS项目中实现JWT认证机制时遇到几个问题:

  1. 如何正确配置JWT模块?我在安装@nestjs/jwt后不太清楚如何在AppModule中进行注册,是否需要额外设置secretKey和过期时间?

  2. 登录成功后生成的Token应该包含哪些标准字段?除了sub和iat之外,是否需要手动添加用户角色权限信息?

  3. 前端请求携带Token时,如何设计拦截器自动将Token注入Authorization头?现在每次手动添加感觉很不规范。

  4. 为什么我的Token验证守卫(@UseGuards)有时候会跳过验证?是不是和路由的顺序有关?

  5. 有没有推荐的安全存储JWT的方案?目前直接存localStorage担心XSS攻击,用httpOnly的Cookie又怕CSRF问题。


3 回复

在NestJS中实现JWT认证机制非常方便。首先安装必要的依赖:

npm install [@nestjs](/user/nestjs)/jwt [@nestjs](/user/nestjs)/passport passport passport-jwt --save
  1. 配置Passport:创建一个Guard用于JWT验证。

    import { Injectable } from '[@nestjs](/user/nestjs)/common';
    import { AuthGuard } from '[@nestjs](/user/nestjs)/passport';
    
    [@Injectable](/user/Injectable)()
    export class JwtAuthGuard extends AuthGuard('jwt') {}
    
  2. 创建JWT策略

    import { ExtractJwt, Strategy } from 'passport-jwt';
    import { PassportStrategy } from '[@nestjs](/user/nestjs)/passport';
    import { Injectable } from '[@nestjs](/user/nestjs)/common';
    
    [@Injectable](/user/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 };
      }
    }
    
  3. 实现登录逻辑并生成JWT

    import { Controller, Post, Body, Res } from '[@nestjs](/user/nestjs)/common';
    import { AuthService } from './auth.service';
    import { Response } from 'express';
    
    [@Controller](/user/Controller)('auth')
    export class AuthController {
      constructor(private authService: AuthService) {}
    
      @Post('login')
      async login(@Body() userDto: { username: string; password: string }, @Res() res: Response) {
        const token = await this.authService.validateUser(userDto);
        if (token) {
          res.status(200).json({ token });
        } else {
          res.status(401).send('Unauthorized');
        }
      }
    }
    
  4. 服务层处理逻辑

    import { Injectable } from '[@nestjs](/user/nestjs)/common';
    import * as bcrypt from 'bcrypt';
    import { JwtService } from '[@nestjs](/user/nestjs)/jwt';
    
    [@Injectable](/user/Injectable)()
    export class AuthService {
      constructor(private jwtService: JwtService) {}
    
      async validateUser(userDto: { username: string; password: string }): Promise<string | null> {
        // 模拟用户验证
        if (userDto.username === 'test' && userDto.password === 'password') {
          const payload = { sub: 1, username: 'test' };
          return this.jwtService.sign(payload);
        }
        return null;
      }
    }
    
  5. 保护路由

    import { Controller, Get, UseGuards } from '[@nestjs](/user/nestjs)/common';
    import { JwtAuthGuard } from './guards/jwt-auth.guard';
    
    [@Controller](/user/Controller)('profile')
    [@UseGuards](/user/UseGuards)(JwtAuthGuard)
    export class ProfileController {
      @Get()
      getProfile() {
        return { message: 'Protected route' };
      }
    }
    

完成以上步骤后,你的NestJS应用就实现了基本的JWT认证功能。


在NestJS项目中实现JWT认证机制,主要分为生成Token、验证Token和拦截器保护路由三个步骤:

  1. 安装依赖:首先安装[@nestjs](/user/nestjs)/jwt[@nestjs](/user/nestjs)/passport以及passport-jwt。命令如下:

    npm install [@nestjs](/user/nestjs)/jwt [@nestjs](/user/nestjs)/passport passport-jwt passport
    
  2. 配置JWT策略:创建一个JWT策略类,用于验证请求中的Token。

    import { ExtractJwt, Strategy } from 'passport-jwt';
    import { PassportStrategy } from '[@nestjs](/user/nestjs)/passport';
    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 };
        }
    }
    
  3. 生成Token:用户登录时生成Token并返回给客户端。

    import { Injectable } from '[@nestjs](/user/nestjs)/common';
    import { JwtService } from '[@nestjs](/user/nestjs)/jwt';
    [@Injectable](/user/Injectable)()
    export class AuthService {
        constructor(private jwtService: JwtService) {}
        async login(user: any) {
            const payload = { username: user.username, sub: user.userId };
            return {
                access_token: this.jwtService.sign(payload),
            };
        }
    }
    
  4. 保护路由:使用@UseGuards装饰器结合JWTGuard来保护需要认证的路由。

    import { Controller, Get, UseGuards } from '[@nestjs](/user/nestjs)/common';
    import { AuthGuard } from '[@nestjs](/user/nestjs)/passport';
    [@Controller](/user/Controller)('protected')
    export class ProtectedController {
        @UseGuards(AuthGuard('jwt'))
        @Get()
        getProtectedData() {
            return { message: 'This is protected data' };
        }
    }
    

通过以上步骤,就可以在NestJS项目中实现基本的JWT认证机制。记得根据实际需求调整秘钥和策略配置。

NestJS JWT认证机制实现

在NestJS中实现JWT(JSON Web Token)认证机制主要包括以下几个步骤:

1. 安装必要依赖

npm install @nestjs/jwt @nestjs/passport passport passport-jwt
npm install @types/passport-jwt --save-dev

2. 创建Auth模块

// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    JwtModule.register({
      secret: 'your-secret-key', // 实际项目中应从配置读取
      signOptions: { expiresIn: '60s' },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

3. 实现Auth服务

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

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

  async validateUser(username: string, pass: string): Promise<any> {
    // 这里应实现实际用户验证逻辑
    if (username === 'admin' && pass === 'password') {
      return { userId: 1, username: 'admin' };
    }
    return null;
  }

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

4. 实现JWT策略

// auth/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', // 应与JwtModule中的一致
    });
  }

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

5. 创建保护路由的守卫

// auth/auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

6. 在控制器中使用

// app.controller.ts
import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { AuthService } from './auth/auth.service';
import { JwtAuthGuard } from './auth/auth.guard';

@Controller()
export class AppController {
  constructor(private authService: AuthService) {}

  @Post('auth/login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }

  @UseGuards(JwtAuthGuard)
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
  }
}

注意事项

  1. 实际项目中应将密钥存储在环境变量中
  2. 考虑使用更复杂的用户验证逻辑
  3. 可以添加刷新令牌机制
  4. 考虑令牌失效处理

这就是在NestJS中实现JWT认证的基本流程。你可以根据需要扩展这个基础实现,添加更多功能如角色权限控制等。

回到顶部