Skip to content

身份认证与授权

使用guard守卫来做权限管理。

在Nest的执行顺序中,守卫处于中间件middleware之后,拦截器interceptor之前。

什么是身份认证?

懒得写。这都不知道吗?

第一步:安装所需库

bash
npm i @nestjs/passport passport

npm i @nestjs/jwt passport-jwt

npm i @types/passport-jwt -D

一句话总结四者关系:

passport 是身份验证的底座框架;@nestjs/passport 是它的 NestJS 适配器;passport-jwt 是底座上的一个"JWT 验证插件";@nestjs/jwt 是独立的 JWT 工具包,负责生成 token 和配置管理。四者缺一不可。

第二步:登录时生成token密钥

  1. 首先创建jwt模块 通常会放在auth.module里面。
// 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';
import { AuthController } from './auth.controller';

@Module({
  imports: [
    PassportModule,
    JwtModule.register({
      secret: 'your-secret-key',     // 建议使用环境变量
      signOptions: { expiresIn: '60m' },
    }),
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
  exports: [AuthService],            // 如果其他模块需要验证 token
})
export class AuthModule {}

警告

请务必在环境变量中配置你的密钥!

  1. 在auth.service里实现登录逻辑,验证用户名密码后签发token
// auth.controller.ts
import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @Post('login')
  async login(@Body() body: { username: string; password: string }) {
    const user = await this.authService.validateUser(body.username, body.password);
    if (!user) {
      throw new UnauthorizedException('用户名或密码错误');
    }
    return this.authService.login(user);
  }
}
// auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

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

  async validateUser(username: string, password: string): Promise<any> {
    // 这里应该查询数据库验证用户名和密码
    if (username === 'admin' && password === 'admin') {
      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),
    };
  }
}

此时访问 POST /auth/login 并且输入正确的用户名与密码,就会收到 access_token 。

第三步:访问时用token获取权限

  1. 创建 JWT 策略(验证逻辑) JWT 策略负责从请求中提取 token、验证有效性并将解析出的用户信息注入 request.user。
// 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',  // 与签发时保持一致,建议环境变量
    });
  }

  async validate(payload: any) {
    // payload 是 token 解码后的内容(如 { sub: userId, username: ... })
    // 这里可查询数据库验证用户是否存在,返回的对象会挂载到 request.user
    return { userId: payload.sub, username: payload.username };
  }
}

jwtFromRequest:ExtractJwt.fromAuthHeaderAsBearerToken():从请求头中使用 Authorization 字段提取 JWT,并且期望格式为 Bearer 。

ignoreExpiration:false 不忽略 JWT 的过期时间,即如果令牌过期,将被视为无效。

secretOrKey:验证 JWT 的密钥

validate:可在validate函数中,做额外的自定义权限校验,例如检查用户状态。这里直接返回参数。

  1. 创建jwt的守卫(guards)保护路由
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

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

Guard 是怎么找到 Strategy 的?

关键就在 AuthGuard('jwt') 这个 'jwt' 字符串。

Passport 内部维护了一个策略注册表(strategy registry)。当你写 extends PassportStrategy(Strategy) 时,passport-jwt 库会自动以 'jwt' 为名把策略注册到这张表里——这个名字是库内部写死的默认名称。

AuthGuard('jwt') 运行时做的事就是:去这张注册表里查名为 'jwt' 的策略,找到后调用它的 validate 方法。所以 Guard 和 Strategy 靠同一个字符串名字实现匹配。

完整链路

请求进来
  → JwtAuthGuard 触发
    → AuthGuard('jwt') 去 Passport 注册表查 'jwt'
      → 找到 JwtStrategy(因为 PassportStrategy(Strategy) 已注册为 'jwt')
        → 执行 super() 中的配置(从 header 取 token、验签)
        → 执行 JwtStrategy.validate(payload)
        → 返回值挂到 req.user
  1. 然后在需要保护的路由上添加 @UseGuards(JwtAuthGuard)。

实例:

import { Controller, Get, UseGuards, Request } from '@nestjs/common';
import { JwtAuthGuard } from './auth/jwt-auth.guard';

@Controller('profile')
export class AppController {
  @UseGuards(JwtAuthGuard) // 这里用的是路由守卫
  @Get()
  getProfile(@Request() req) {
    return req.user;   // { userId: 1, username: 'admin' }
  }
}