跳转到内容

授权模块

基于 JWT + Passport 的认证授权模块,支持 Bearer Token 和自定义 Header (access-token) 提取。

模块注册

typescript
import { AuthModule } from '@maxtan/nest-core'

@Module({
  imports: [
    AuthModule.register({
      secret: 'your-jwt-secret-key',
      signOptions: {
        expiresIn: '72h',   // Token 有效期(默认 72h)
        algorithm: 'HS256', // 签名算法(默认 HS256)
        issuer: 'boot',     // 发行方
        audience: 'client'  // 目标
      },
      global: true           // 全局注册(默认 true)
    })
  ]
})
export class AppModule {}

保护路由

手动应用 AuthGuard

typescript
import { Controller, Get, UseGuards } from '@nestjs/common'
import { AuthGuard } from '@maxtan/nest-core'

@Controller('protected')
@UseGuards(AuthGuard)
export class ProtectedController {
  @Get()
  getData() {
    return { message: '受保护的数据' }
  }
}

全局应用 AuthGuard

typescript
import { APP_GUARD } from '@nestjs/core'
import { AuthGuard, AuthModule } from '@maxtan/nest-core'

@Module({
  imports: [AuthModule.register({ secret: 'your-secret' })],
  providers: [
    { provide: APP_GUARD, useClass: AuthGuard }
  ]
})
export class AppModule {}

公开路由

全局启用 AuthGuard 后,用 @AuthPublic() 标记无需认证的路由:

typescript
import { AuthPublic } from '@maxtan/nest-core'

@Controller()
export class PublicController {
  @Get('health')
  @AuthPublic()
  healthCheck() {
    return { status: 'ok' }
  }
}

获取用户信息

AuthDecorator 装饰器

typescript
import { AuthDecorator } from '@maxtan/nest-core'

@Controller('profile')
export class ProfileController {
  // 获取完整用户信息
  @Get()
  getProfile(@AuthDecorator() user: AuthPayload) {
    return user
  }

  // 获取指定字段
  @Get('name')
  getName(@AuthDecorator('username') username: string) {
    return { username }
  }

  // 获取解密后的数据(如果使用了 AES 加密 payload)
  @Get('data')
  getData(@AuthDecorator('decryptedData') data: any) {
    return data
  }
}

JWT Payload 结构

typescript
interface AuthPayload {
  // JWT 标准字段
  sub?: string          // Subject(通常是用户 ID)
  exp?: number          // 过期时间
  iat?: number          // 签发时间
  iss?: string          // 发行方
  aud?: string | string[] // 目标

  // 业务字段
  userId?: string       // 用户 ID
  username?: string     // 用户名
  account?: string      // 账号

  // AES 加密数据
  jtd?: string          // 加密的 JSON Token Data
  decryptedData?: any   // 解密后的完整数据

  // 扩展字段
  [key: string]: any
}

AES 加密 Payload

AuthStrategy 内置了 AES 加密 payload 解密支持。在签发 Token 时,将敏感数据加密到 jtd 字段:

typescript
import { encryptAES } from '@maxtan/nest-core'

// 签发 Token
const userData = { id: '123', name: 'John', roles: ['admin'] }
const payload = {
  sub: userData.id,
  jtd: encryptAES(JSON.stringify(userData), jwtSecret)
}
const token = jwtService.sign(payload)

AuthStrategy 自动解密 jtd 字段,结果存入 payload.decryptedData,并映射 iduserIdnameusername

解密结果使用 LRU 缓存(最多 1000 条,5 分钟过期),避免重复解密。

配置参考

AuthModuleOptions

参数类型默认值说明
secretstring-JWT 签名密钥(必填)
globalbooleantrue是否全局注册
signOptionsJwtSignOptions见下JWT 签名选项

默认签名选项:

typescript
{
  algorithm: 'HS256',
  expiresIn: '72h',
  issuer: 'boot',
  audience: 'client'
}

Token 提取方式

AuthStrategy 支持两种方式提取 Token(按优先级):

  1. Authorization: Bearer <token> — 标准 Bearer Token
  2. access-token: <token> — 自定义 Header

最佳实践

  1. 全局启用 AuthGuard:通过 APP_GUARD 全局注册,用 @AuthPublic() 标记公开路由,避免遗漏保护
  2. JWT Secret 从环境变量读取:使用 validateEnv 校验并从 process.env 读取,不要硬编码
  3. AES 加密 Payload:敏感信息使用 encryptAES 加密到 jtd 字段,AuthStrategy 自动解密 + LRU 缓存
  4. Token 有效期:根据场景配置 expiresIn,API 推荐 72h,后台管理可适当延长
  5. 公开路由最小化@AuthPublic() 仅用于健康检查、登录等真正公开的端点
  6. 请求上下文自动传递AuthModule 内置 ClsModulenestjs-cls)和 RequestContextInterceptor,自动将 request.user 写入 CLS 上下文,任何地方可通过 getCurrentUserId() / getCurrentUser() 获取当前用户
  7. 非鉴权场景自动降级@AuthPublic() 路由或定时任务等无 Token 场景,getCurrentUserId() 返回 null,审计中间件使用 fallbackUserId(如 'system'),不会报错
typescript
import { getCurrentUserId, getCurrentUser } from '@maxtan/nest-core'

// 在任意 Service / 工具函数中获取当前用户
const userId = getCurrentUserId()   // string | null(公开路由/定时任务返回 null)
const user = getCurrentUser()       // AuthPayload | undefined
typescript
// 推荐的环境变量配置方式
import { validateEnv, zEnvStr } from '@maxtan/nest-core'

const env = validateEnv(z.object({
  JWT_SECRET: zEnvStr().pipe(z.string().min(16, 'JWT_SECRET 至少 16 位')),
}))

AuthModule.register({ secret: env.JWT_SECRET })

相关文档