nestjs-RBAC权限控制
- 创建表
- 如果有菜单、资源等,需要创建菜单表
menus与权限的映射表roles_menus
role
- 创建枚举
export enum Role {
User = 2,
Admin = 1
}
- 创建装饰器
SetMetadata可以将枚举Role的内容缓存,相当于pinio
import { SetMetadata } from '@nestjs/common'
import { Role } from 'src/enum/roles.enum'
export const ROLES_KEY = 'roles'
// 装饰器Roles SetMetadata将装饰器的值缓存
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles)
- 创建守卫
nest g gu guards/role --no-spec
- 从
req中获取req.user.username需要配合nestjs的jwt的守卫(经过后会从token中解析出user的值放到req.user中),如下,
@UseGuards(JwtGuard, RoleGuard)
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { ROLES_KEY } from 'src/decorators/roles.decorator'
import { Role } from 'src/enum/roles.enum'
import { UserService } from 'src/user/user.service'
@Injectable()
export class RoleGuard implements CanActivate {
constructor(
private reflector: Reflector,
private readonly userService: UserService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
// 1.通过反射获取到装饰器的权限
// getAllAndOverride读取路由上的metadata getAllAndMerge合并路由上的metadata
const requireRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
context.getHandler(),
context.getClass()
])
// 2.获取req拿到鉴权后的用户数据
const req = context.switchToHttp().getRequest()
// 3.通过用户数据从数据查询权限
const user = await this.userService.findOneByName(req.user.username)
const roleIds = user.roles.map((item) => item.id)
// 4.判断用户权限是否为装饰器的权限 的some返回boolean
const flag = requireRoles.some((role) => roleIds.includes(role))
return flag
}
}
- 在
controller使用
- 方法的
@Roles注解会覆盖控制器的注解
...
@Controller('roles')
@Roles(Role.Admin) // 装饰器会缓存Role.Admin的值 RoleGuard中获取缓存的值进行守卫鉴权
@UseGuards(JwtGuard, RoleGuard)
export class RolesController {
constructor(private readonly rolesService: RolesService) {}
...
@Get(':id')
@Roles(Role.User)
findOne(@Param('id') id: string) {
return this.rolesService.findOne(+id)
}
...
}