Nestjs实现RBAC权限控制的实现

in nestjsnodejs with 19 comments

1、项目的创建这些我们略过,如果还不会的同学,直接看文档。
2、jwt验证的实现,同样的 直接跟着文档来:https://docs.nestjs.com/security/authentication#jwt-functionality
3、user的entity这里改动下:

user/entities/user.entity.ts

import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  BeforeInsert,
  ManyToMany,
  JoinTable
} from 'typeorm';
import * as bcrypt from 'bcrypt';
import { Exclude, Transform } from 'class-transformer';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 100, unique: true })
  username: string;

  @Column({ length: 100, unique: true })
  email: string;

  @Column({ nullable: true, length: 100 })
  name: string;

  @Column()
  @Exclude({ toPlainOnly: true })
  password: string;

  @Column({ default: true })
  is_active: boolean;

  @Column({ default: false })
  is_super: boolean;


  
  @ManyToMany(() => Group)
  @JoinTable()
  groups: Group[];

  @BeforeInsert()
  async hashPassword() {
    this.password = await bcrypt.hash(this.password, 10);
  }

  async set_password(password: string) {
    this.password = await bcrypt.hash(password, 10);
  }

  async check_password(password: string): Promise<boolean> {
    return await bcrypt.compare(password, this.password);
  }

  has_group(group:string){
    let dt = false
    this.groups.forEach(function(g){
       if(g.name === group){
          dt= true;
          return false
       }
    })
    return dt
  }

  has_permission(permission:string){
    let dt = false
    this.groups.forEach(function(group){
      if(group.has_permission(permission)){
        dt = true;
        return false
      }
    })
    return dt
  }

}

@Entity()
export class Permission {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 100, unique: true  })
  name: string;

  @Column({ length: 100, default: '' })
  desc: string;
}

@Entity()
export class Group {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 100, unique: true  })
  name: string;

  @Column({ length: 100, default: '' })
  desc: string;

  @ManyToMany(() => Permission, {  eager: true,  })
  @JoinTable()
  permission: Permission[];

  has_permission(permission:string){
    let dt = false
    this.permission.forEach(function(perm): boolean{
      if(perm.name === permission){
        dt = true
        return false
      } 
    })
    return dt
  }
}

这里涉及的是user跟group manytomany的关系,以及group跟permission的manytomany关系。
group包含has_permission方法,验证权限。
user包含has_group和has_permission方法,验证组以及权限。

4、在auth/jwt.strtegy.ts中修改:

import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
+import { UserService } from '../user/services'

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly userService: UserService) { 
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: process.env.JWT_SECRET,
    });
  }

   
  async validate(payload: any) {
    -const user = {username:payload.username, userId:payload.sub}
    +const user = await this.userService.get(payload.sub)
    return user;
  }
}

官方的文档在jwt的validate只是简单的返回了jwt_token解密的userID和username.这里我们需要验证数据库并且返回User对象,这个User对象用于在request中,通过他的has_group方法以及has_permission方法来验证权限和组。

新增auth/role.permission.guard.ts,用于验证权限。

import {
    Injectable,
    CanActivate,
    ExecutionContext,
    UnauthorizedException,
    SetMetadata
  } from '@nestjs/common';
  import { Reflector } from '@nestjs/core';
  

  
  export const ermission_required = (permission: string) => SetMetadata('permission', permission);

  @Injectable()
  export class PermissionGuard implements CanActivate {
    constructor(private readonly reflector: Reflector) {}
  
    canActivate(context: ExecutionContext): boolean {
      const permission = this.reflector.get<string>('permission', context.getHandler());
      if (!permission) {
        return false;
      }
      const request = context.switchToHttp().getRequest();
      const user = request.user; // is undefined
      // console.log(user,roles)
      if (user && user.groups) { 
        // return roles.includes(user.groups);
        return user.has_permission(permission)
      } else {
        throw new UnauthorizedException();
      }
    }
  }

用法:

+import { PermissionGuard, Permission_required } from './auth/role/perms.guard'

@UseGuards(JwtAuthGuard, PermissionGuard)
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

   
   
  @Get('profile')
  @Permission_required('can_add_user')  #权限needed
  profile(@Request() request){
    return request.user
  }
 
}
Comments are closed.