1-5-1 NestJS 鉴权库:JWT 核心文档解析
本节系统梳理 NestJS 集成 JWT 鉴权的完整技术栈,解读官方文档中涉及的核心概念:Passport 策略、AuthGuard 守卫、JwtModule 配置、JwtService 签名,以及各层级之间的协作关系。
鉴权模型:房卡与房间的比喻
用户登录(出示身份证:用户名 + 密码)
│
▼
服务器验证身份,签发房卡(JWT Token)
│
▼
用户持房卡访问各个房间(受保护的 API 接口)
│
├── 房卡有效 → 允许进入(请求通过)
└── 房卡无效/过期 → 拒绝进入(返回 401)
text
需要安装的依赖
NestJS 官方文档 JWT 功能章节要求安装以下依赖:
# JWT 核心模块
pnpm add @nestjs/jwt
# Passport 认证框架 + JWT 策略
pnpm add @nestjs/passport passport passport-jwt
# TypeScript 类型定义
pnpm add -D @types/passport-jwt
bash
| 包名 | 作用 |
|---|---|
@nestjs/jwt | NestJS 官方 JWT 模块,封装签名和验证方法 |
@nestjs/passport | NestJS 与 Passport 框架的桥接层 |
passport | Node.js 认证中间件,提供策略化认证机制 |
passport-jwt | Passport 的 JWT 策略实现 |
核心概念解读
1. Passport:策略化认证框架
Passport 是 Node.js 生态中最流行的认证中间件,核心理念是策略(Strategy)模式:
Passport 框架
├── passport-local → 用户名 + 密码认证
├── passport-jwt → JWT Token 认证
├── passport-oauth2 → OAuth 2.0 认证
├── passport-google → Google 登录
└── ... → 500+ 种策略
text
每种认证方式对应一个策略库,按需安装 passport-<strategy-name> 即可。Passport 将加密/解密/算法等复杂逻辑封装为黑盒,开发者只需实现 validate 方法。
NestJS 的封装层:@nestjs/passport 将 Passport 的回调式(callback)API 转换为面向对象风格,统一暴露 validate() 方法:
// Passport 原生写法(回调式)
passport.use(new LocalStrategy((username, password, done) => {
// 校验逻辑
done(null, user); // 或 done(null, false)
}));
// NestJS 封装写法(面向对象)
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
async validate(username: string, password: string) {
// 校验逻辑,返回 user 或抛出 UnauthorizedException
}
}
typescript
关键统一性:无论是
LocalStrategy还是JwtStrategy,NestJS 中的写法完全一致——都继承PassportStrategy,都实现validate方法。
2. JwtModule:JWT 模块配置
JWT 模块需要在 AuthModule 中注册,配置密钥和过期时间:
// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';
import { jwtConstants } from './constants';
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: jwtConstants.secret, // 签名密钥
signOptions: { expiresIn: '60s' }, // Token 过期时间
}),
],
providers: [AuthService],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
typescript
配置项说明:
| 参数 | 说明 |
|---|---|
secret | 签名密钥,服务端持有,用于签名和验证 Token |
signOptions.expiresIn | Token 过期时间(如 '60s'、'7d'、'1h') |
global | 设为 true 时,JwtModule 全局可用,无需在每个模块重复导入 |
常量文件(建议):
// auth/constants.ts
export const jwtConstants = {
secret: process.env.JWT_SECRET || 'your-secret-key', // 生产环境使用环境变量
};
typescript
3. JwtService:Token 签名与验证
JwtService 由 JwtModule 自动提供,通过依赖注入在 AuthService 中使用:
// auth/auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async signIn(username: string, password: string) {
// 1. 验证用户
const user = await this.usersService.findOne(username);
if (!user || user.password !== password) {
throw new UnauthorizedException();
}
// 2. 签发 JWT
const payload = { sub: user.userId, username: user.username };
return {
access_token: await this.jwtService.signAsync(payload),
};
}
}
typescript
JWT Payload 关键字段:
| 字段 | 说明 |
|---|---|
sub | Subject,存储用户 ID,遵循 JWT 标准约定 |
username | 自定义字段,存储用户名 |
iat | Issued At,自动添加的签发时间 |
exp | Expiration,自动添加的过期时间 |
sub是 JWT RFC 7519 标准中定义的保留字段,用于唯一标识主体(用户)。
4. AuthGuard:路由守卫
守卫(Guard)用于保护路由,只有通过认证的请求才能访问:
// auth/auth.controller.ts
import { Controller, Get, Post, Body, UseGuards, Request } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth')
export class AuthController {
@Post('login')
login(@Body() body: { username: string; password: string }) {
return this.authService.signIn(body.username, body.password);
}
@UseGuards(AuthGuard('jwt'))
@Get('profile')
getProfile(@Request() req) {
return req.user; // req.user 由 JwtStrategy.validate() 返回
}
}
typescript
守卫工作流程:
请求到达 /auth/profile
│
▼
AuthGuard('jwt') 拦截
│
▼
从 Authorization Header 提取 Bearer Token
│
▼
JwtStrategy.validate() 验证 Token
│
┌───┴───┐
│ │
验证通过 验证失败
│ │
▼ ▼
req.user 返回 401 Unauthorized
= payload
│
▼
Controller 处理请求
text
5. JwtStrategy:JWT 验证策略
// auth/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { jwtConstants } from './constants';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: jwtConstants.secret,
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
typescript
配置项说明:
| 参数 | 说明 |
|---|---|
jwtFromRequest | Token 提取方式(从 Authorization Header 的 Bearer 中提取) |
ignoreExpiration | 是否忽略过期检查(生产环境必须为 false) |
secretOrKey | 验证签名的密钥,必须与签发时一致 |
Refresh Token 机制(概念介绍)
| Token 类型 | 有效期 | 用途 |
|---|---|---|
| Access Token(JWT) | 较短(如 1 小时) | 访问受保护的 API |
| Refresh Token | 较长(如 7 天) | 用于获取新的 Access Token |
Refresh Token 的设计目的是减少 Access Token 泄露的风险:短期 Token 即使被窃取,有效期也很短。Refresh Token 通常存储在 HttpOnly Cookie 中,安全性更高。
技术栈层级关系
@nestjs/jwt
├── JwtModule.register() → 模块配置(secret, expiresIn)
├── JwtService → 注入到 AuthService
│ ├── signAsync(payload) → 签发 Token
│ └── verifyAsync(token) → 验证 Token
└── 依赖 jsonwebtoken 库 → 底层加密算法
@nestjs/passport
├── PassportStrategy → 策略基类
│ ├── LocalStrategy → 用户名密码认证
│ └── JwtStrategy → JWT Token 认证
├── AuthGuard('jwt') → 路由守卫装饰器
└── 依赖 passport 框架 → 策略模式中间件
text
本节要点
- Passport 框架:策略化认证中间件,每种认证方式对应一个策略库,
@nestjs/passport将其封装为面向对象风格 - NestJS 统一接口:所有策略(Local、JWT 等)都通过
PassportStrategy基类 +validate()方法统一使用 - JwtModule 配置:注册密钥(secret)和过期时间(expiresIn),自动提供
JwtService - JwtService.signAsync():封装 JWT 签名逻辑,无需手动处理加密算法
- AuthGuard 守卫:在路由处理前拦截请求,验证 Token 有效性
- JwtStrategy.validate():定义 Token 验证通过后返回的用户信息结构
↑