基于角色的权限控制例如角色权限admin增删改查user只能查看guest无权限先理解 RBAC当前登录 ↓ JWT ↓ 认证成功只能证明你是谁但是不知道你能干什么RBAC解决的是你能访问哪些资源最终效果普通用户DELETE /books/1返回{ statusCode:403, message:Forbidden }管理员DELETE /books/1成功{ message:删除成功 }第一步给 User 增加角色修改src/users/entities/user.entity.ts现在Entity() export class User { PrimaryGeneratedColumn() id:number; Column({ unique:true, }) username:string; Column() password:string; }增加Column({ default:user, }) role:string;完整Entity() export class User { PrimaryGeneratedColumn() id:number; Column({ unique:true, }) username:string; Column() password:string; Column({ default:user, }) role:string; }数据库会变useridusernamepasswordrole1adminxxxadmin2tomxxxuser第二步注册时默认 user修改users.service.ts创建用户const user this.userRepository.create({ username, password, role:user, });后面可以做后台创建管理员暂时手动修改数据库。例如update user set roleadmin where id1;第三步登录时携带角色当前const payload { sub:user.id, username:user.username, }改const payload { sub:user.id, username:user.username, role:user.role, }JWT{ sub:1, username:admin, role:admin }第四步创建 Roles 装饰器目录src/auth/decorators新增roles.decorator.ts内容import { SetMetadata } from nestjs/common; export const ROLES_KEY roles; export const Roles ( ...roles:string[] ) SetMetadata( ROLES_KEY, roles, );使用Roles(admin)等价roles:[admin]第五步创建 RolesGuard目录src/auth/guards新增roles.guard.ts内容import { CanActivate, ExecutionContext, Injectable } from nestjs/common; import { Reflector } from nestjs/core; import { ROLES_KEY } from ../decorators/roles.decorator; import { JwtPayload } from ../interfaces/jwt-payload.interface; Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext) { const roles this.reflector.getAllAndOverridestring[](ROLES_KEY, [ context.getHandler(), context.getClass(), ]); if (!roles) { return true; } const request context.switchToHttp().getRequest{ user: JwtPayload; }(); const user request.user; return roles.includes(user.role); } }新增src\auth\interfaces\jwt-payload.interface.tsexport interface JwtPayload { sub: number; username: string; role: string; }逻辑读取 Roles() ↓ 获取 request.user.role ↓ 比较 ↓ 通过 / 拒绝第六步注册全局 RolesGuard打开app.module.ts增加import { RolesGuard } from ./auth/guards/roles.guard;providersproviders:[ AppService, { provide:APP_GUARD, useClass:JwtAuthGuard, }, { provide:APP_GUARD, useClass:RolesGuard, }, ]执行顺序JwtAuthGuard ↓ RolesGuard ↓ Controller第七步给接口加角色限制例如books.controller.ts删除图书Roles(admin) Delete(:id) remove( Param(id) id:string, ){ return this.booksService.remove( id, ); }查看图书Get() findAll(){ return this.booksService.findAll(); }不限制。第八步测试用户数据库roleuser登录{ access_token:xxx }请求DELETE /books/1返回{ statusCode:403, message:Forbidden resource }管理员数据库roleadmin登录{ access_token:xxx }请求DELETE /books/1成功。进一步优化推荐不要用字符串Roles(admin)创建// src/users/enums/role.enum.ts export enum Role { ADMINadmin, USERuser, }使用import { Role } from ../enums/role.enum;User 实体Column({ default: Role.USER, }) role: Role;Roles 装饰器Roles(Role.ADMIN)你当前项目推荐结构src ├── auth │ ├── decorators │ ├── guards │ ├── interfaces │ │ └── jwt-payload.interface.ts │ ├── users │ ├── entities │ │ └── user.entity.ts │ ├── enums │ │ └── role.enum.ts │ ├── booksrole.enum.tsexport enum Role { ADMIN admin, USER user, }user.entity.tsimport { Role } from ../enums/role.enum; Column({ type: enum, enum: Role, default: Role.USER, }) role: Role;roles.decorator.tsimport { Role } from ../../users/enums/role.enum; export const Roles (...roles: Role[]) SetMetadata(ROLES_KEY, roles);books.controller.tsRoles(Role.ADMIN) Delete(:id) remove(Param(id) id: string) { return this.booksService.remove(id); }这样从现在开始你整个 RBAC 系统就都是强类型的Role.ADMIN Role.USER而不是admin user避免拼写错误导致权限失效。RBAC完善支持多角色实际上你前面写的Roles()已经天然支持多角色。因为export const Roles (...roles: Role[]) SetMetadata( ROLES_KEY, roles, );这里...roles就是剩余参数。例如Roles(Role.ADMIN)得到[admin]例如Roles( Role.ADMIN, Role.USER, )得到[ admin, user, ]你的 Guardreturn roles.includes( user.role, );已经支持多角色。所以Roles( Role.ADMIN, Role.USER, ) Get() findAll() {}表示admin 可以访问 或 user 可以访问实现 CurrentUser 装饰器目前你可能这样拿用户Get() findAll( Req() req, ){ console.log(req.user); }不优雅。Nest 推荐封装装饰器。创建目录src └── auth └── decorators └── current-user.decorator.ts编写装饰器import { createParamDecorator, ExecutionContext, } from nestjs/common; import { Request } from express; export const CurrentUser createParamDecorator( ( data: unknown, ctx: ExecutionContext, ) { const request ctx .switchToHttp() .getRequestRequest(); return request.user; }, );给 Request.user 类型否则会报Property user does not exist on type Request创建src └── types └── express.d.ts内容import { JwtPayload } from ../auth/interfaces/jwt-payload.interface; declare global { namespace Express { interface Request { user: JwtPayload; } } } export {};完善 JwtPayload你应该已经有src/auth/interfaces/jwt-payload.interface.ts内容import { Role } from ../../users/enums/role.enum; export interface JwtPayload { sub: number; username: string; role: Role; }完善 JwtStrategy现在validate( payload: JwtPayload, ){ return payload; }Controller 使用例如import { CurrentUser } from ../auth/decorators/current-user.decorator; import { JwtPayload } from ../auth/interfaces/jwt-payload.interface;Get() findAll( CurrentUser() user: JwtPayload, ){ console.log(user); return this.booksService.findAll(); }请求GET /books Authorization: Bearer xxx打印{ sub: 1, username: admin, role: admin }直接获取字段高级写法改造装饰器export const CurrentUser createParamDecorator( ( data: keyof JwtPayload, ctx: ExecutionContext, ) { const request ctx.switchToHttp().getRequest(); const user request.user; return data ? user[data] : user; }, );然后获取整个用户CurrentUser() user: JwtPayload得到{ sub:1, username:admin, role:admin }只获取用户名CurrentUser(username) username: string得到admin只获取角色CurrentUser(role) role: Role得到admin测试示例Roles( Role.ADMIN, Role.USER, ) Get(profile) profile( CurrentUser() user: JwtPayload, ){ return user; }请求GET /books/profile Authorization: Bearer xxx返回{ sub: 1, username: admin, role: admin }到这里你的认证授权体系已经比较完整JWT ↓ JwtStrategy ↓ CurrentUser ↓ JwtAuthGuard ↓ RolesGuard ↓ RBAC现在你的权限体系JWT认证 ↓ JwtAuthGuard ↓ 获取用户 ↓ RolesGuard ↓ 角色校验 ↓ Controller这已经是很多企业后台管理系统的基础权限架构了。