使用Nestjs实现基于JWT + RBAC的认证授权系统

前言

这篇文章使用Nestjs实现了一个基于JWT+RBAC(Role-Based Access Control)的认证授权系统,主要包含以下功能:

  • 用户认证:实现用户登录、登出等基础认证功能
  • 权限控制:基于RBAC模型的细粒度权限控制
  • JWT认证:使用JWT实现无状态的用户身份验证

数据库设计

实体关系图

erDiagram SysUsersEntity ||--o{ UsersRoleEntity : has RoleEntity ||--o{ UsersRoleEntity : has SysUsersEntity { number id PK string loginName string password string status number tokenVersion } RoleEntity { number id PK string name } UsersRoleEntity { number admin_id PK number role_id PK }

表结构说明

1. 用户表 (ibuy_admin)

字段名 类型 说明 约束
id number 用户ID 主键
login_name string 登录名 唯一,可空
password string 密码 可空
status string 用户状态 可空
tokenVersion number token版本号 默认值0

2. 角色表 (ibuy_role)

字段名 类型 说明 约束
id number 角色ID 主键
name string 角色名称 唯一,可空

3. 用户角色关联表 (ibuy_admin_role)

字段名 类型 说明 约束
admin_id number 用户ID 联合主键
role_id number 角色ID 联合主键

系统配置

环境变量

需要在.env文件中配置以下环境变量:

env 复制代码
JWT_SECRET=your_jwt_secret_key

核心功能

1. 认证流程

1.1 登录认证

用户登录时,系统会:

  1. 验证用户名和密码
  2. 生成包含用户信息的JWT Token
  3. 返回Token和用户信息
typescript 复制代码
// auth.controller.ts
@Public()
@HttpCode(HttpStatus.OK)
@Post('login')
async signIn(@Body() user: Record<string, any>) {
  return this.authService.signIn(user.loginName, user.password);
}

1.2 JWT认证守卫

系统使用全局AuthGuard进行Token验证:

typescript 复制代码
// auth.guard.ts
@Injectable()
export class AuthGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 检查是否是公开接口
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) return true;

    // 验证Token
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException();
    }

    try {
      const payload = await this.jwtService.verifyAsync(token);
      request.user = payload;
    } catch {
      throw new UnauthorizedException();
    }
    return true;
  }
}

2. RBAC权限控制

2.1 角色权限守卫

使用PermissionGuard实现基于角色的权限控制:

typescript 复制代码
// permission.guard.ts
@Injectable()
export class PermissionGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    if (!request.user) return true;

    // 获取用户角色
    const roleIds = await this.usersRoleService.findRolesIdByUserId(request.user.user_id);
    const roles = await this.roleService.findRolesByRoleIds(roleIds.data);
    const roleNames = roles.data?.map((role) => role.name);

    // 检查权限
    const requiredRoles = this.reflector.getAllAndOverride(PERMISSION_KEY, [
      context.getClass(),
      context.getHandler(),
    ]);

    return requiredRoles?.some((role) => roleNames?.includes(role)) ?? true;
  }
}

2.2 权限装饰器

提供@Permission装饰器用于声明接口所需的角色权限:

typescript 复制代码
// permission.decorator.ts
export const PERMISSION_KEY = 'permission_by_roles';
export const Permission = (...permissions: string[]) =>
  SetMetadata(PERMISSION_KEY, permissions);

3. 使用示例

3.1 接口权限控制

typescript 复制代码
// role.controller.ts
@Permission('admin')  // 声明需要admin角色才能访问
@Controller('role')
export class RoleController {
  @Post()
  createRole(@Body() createRoleDto: CreateRoleDto) {
    return this.roleService.createRole(createRoleDto);
  }
}

3.2 全局配置

在AppModule中注册全局守卫:

typescript 复制代码
// app.module.ts
@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
    {
      provide: APP_GUARD,
      useClass: PermissionGuard,
    },
  ],
})
export class AppModule {}

注意事项

  1. 所有需要认证的接口都会自动经过AuthGuard验证
  2. 使用@Public()装饰器可以将接口标记为公开接口
  3. 使用@Permission()装饰器可以指定访问接口所需的角色
  4. 角色验证基于RBAC模型,支持多角色
相关推荐
Anlici22 分钟前
跨域解决方案还有优劣!?
前端·面试
uhakadotcom24 分钟前
MaxCompute Python UDF开发指南:从入门到精通
后端·面试·github
庸俗今天不摸鱼28 分钟前
【万字总结】构建现代Web应用的全方位性能优化体系学习指南(二)
前端·性能优化·webp
追寻光1 小时前
Java 绘制图形验证码
java·前端
前端snow1 小时前
爬取数据利用node也行,你知道吗?
前端·javascript·后端
村头一颗草1 小时前
高德爬取瓦片和vue2使用
前端·javascript·vue.js
远山无期1 小时前
vue3+vite项目接入qiankun微前端关键点
前端·vue.js
陈随易1 小时前
告别Node.js:2025年,我为何全面拥抱Bun
前端·后端·程序员
还是鼠鼠1 小时前
Node.js--exports 对象详解:用法、示例与最佳实践
前端·javascript·vscode·node.js·web
CQU_JIAKE1 小时前
2.5[frontEnd]
前端