umi + Nest.js 实现高效权限管理系统

umimax + Nest.js 实现权限管理系统

前端

在 umi 文档中,我们可以看到有一个称为权限配置的功能,它提供了一套自己的权限管理方案。

权限定义

我们约定将 src/access.ts 作为权限定义文件,在其中可以定义和判断用户是否具有某个权限。

路由权限

通过在路由配置中添加 access 字段,可以判断路由是否有权限访问。

功能权限

使用 useAccess 来获取权限,可以获得所有定义的权限。

获取用户权限信息时机

我们约定在 src/app.tsx 中导出一个 getInitialState 方法,用于初始化信息。例如:

ini 复制代码
export async function getInitialState(): Promise<{
  user_info?: API.UserInfo;
  roles?: any[];
}> {
  let user_info;
  let roleList;
  const fetchUserInfo = async () => {
    try {
      const token = localStorage.getItem('token');
      const user_info = JSON.parse(localStorage.getItem('user_info') || '');
      if (!token || !user_info) {
        history.push('/login');
      }
      return { token, user_info };
    } catch (error) {
      history.push('/login');
    }
    return { token: null, user_info: null };
  };
  if (history.location.pathname !== '/login') {
    const res = await fetchUserInfo();
    const { roles, ...reset } = res.user_info;
    user_info = reset;
    roleList = roles;
  }
  return {
    user_info,
    roles: roleList,
  };
}

上述代码中,我们在初始化时判断是否为登录页,从而获取存储的信息(权限)。但是,如果登录信息发生改变,则需要重新获取最新的登录信息。因此,在登录时,我们需要重新初始化:

typescript 复制代码
import { useModel } from '@umijs/max';
​
const { refresh } = useModel('@@initialState');
const handleLogin = async (values: any) => {
  const res = await login(values);
  if (res.data) {
    const { token, user_info } = res.data;
    localStorage.setItem('token', token);
    localStorage.setItem('user_info', JSON.stringify(user_info));
    navigate('/');
    refresh();
  }
};

登录成功后,更新本地缓存并重新初始化,即可获取用户最新的权限。

后端

用户 => 角色 => 权限

用户、角色、权限三个表相互关联。

数据库

user.entity.ts

less 复制代码
import { Role } from 'src/role/entities/role.entity';
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
​
@Entity({
  name: 'user',
})
export class User {
  @PrimaryGeneratedColumn()
  id: number;
​
  @Column({
    comment: '姓名',
    length: 50,
  })
  name: string;
​
  @Column({
    comment: '密码',
    length: 50,
  })
  password: string;
​
  @Column({
    comment: '头像',
    length: 1000,
    default: 'https://p26-passport.byteacctimg.com/img/user-avatar/312989b46037c16843b1eb44aea82fa2~180x180.awebp?',
  })
  avator: string;
​
  @Column({
    comment: '手机号',
    length: 50,
    default:""
  })
  mobile: string;
​
  @ManyToMany(() => Role)
  @JoinTable({
    name: 'user_role_relation',
  })
  roles: Role[]; // 角色
​
  @Column({
    comment: '是否可用',
    default: true,
  })
  state: boolean;
}

role.entity.ts

less 复制代码
import {
    Column,
    CreateDateColumn,
    Entity,
    JoinTable,
    ManyToMany,
    PrimaryGeneratedColumn,
    UpdateDateColumn,
  } from 'typeorm';
  import { Permission } from '../../permission/entities/permission.entity';
  @Entity()
  export class Role {
    @PrimaryGeneratedColumn()
    id: string;
  
    @Column({
      length: 20,
    })
    name: string;
  
    @CreateDateColumn()
    createTime: Date;
  
    @UpdateDateColumn()
    updateTime: Date;
  
    @ManyToMany(() => Permission)
    @JoinTable({
      name: 'role_permission_relation',
    })
    permissions: Permission[];
  }
  

permission.entity.ts

less 复制代码
import {
    Column,
    CreateDateColumn,
    Entity,
    PrimaryGeneratedColumn,
    UpdateDateColumn,
  } from 'typeorm';
  
  @Entity()
  export class Permission {
    @PrimaryGeneratedColumn()
    id: string;
  
    @Column({
      length: 50,
    })
    name: string;
  
    @Column({
      length: 100,
      nullable: true,
    })
    desc: string;
​
    @CreateDateColumn()
    createTime: Date;
  
    @UpdateDateColumn()
    updateTime: Date;
  }
  

定义好三个表后,基本完成。

在查询用户信息时,我们也需要查询用户的角色和权限:

php 复制代码
 this.userRepository.findOne({
      where: { id, state: true },
      select: ['name', 'avator', 'id', 'mobile'],
      relations: ['roles', 'roles.permissions'],
    });

更新用户角色:

javascript 复制代码
async updateUserRoles(createRoleDto) {
    const id = createRoleDto.userId;
​
    const user = await this.findOne(id);
    if (!user) {
      throw new UnauthorizedException('用户名不存在');
    }
    console.log(user);
​
    // 查询传入数组roles的全部role实体
    const roles = await this.roleRepository.findBy({
      id: In(createRoleDto.roleIds),
    });
    user.roles = roles
    if (roles.length <= 0) {
      throw new Error('roles not found');
    }
​
    return this.userRepository.save(user);
  }

权限和角色的查询和修改与用户表类似。通过这样的设计,权限控制就可以实现了。

总结

以上便是我开发的后台管理系统(umimax + Nest + MySQL)的权限设计。通过这个项目,我学习了后端、数据库知识,以及 Docker 和自动化部署。如果你对这些感兴趣,欢迎一起学习,项目地址:umimax+nest+mysql

相关推荐
GreenTea3 小时前
GrokBot 核心成员 Lauren Tan:每月交付 2000 个 PR 的人,是怎么用 AI 的
前端·后端·架构
码事漫谈3 小时前
FDE:一个缩写,两种命运
后端
峰向AI3 小时前
Kage: 一个 244KB 文件,做出百万级视觉效果
github
mCell4 小时前
用 Knip 清理 AI Coding 留下的冗余代码
前端·ai编程·前端工程化
名字还没想好☜4 小时前
Go context.AfterFunc 实战(Go 1.21):context 一取消就自动跑清理,告别手写 goroutine 监听 Done
后端·golang·go
笃行3505 小时前
使用Rokid AIUI做了一个童年推箱子游戏
前端
明月_清风5 小时前
为什么最近开始关注 JEV?几个实战案例告诉你答案
人工智能·后端
GetcharZp5 小时前
5 分钟拥有自己的 S3:RustFS 上手(MinIO 的 Rust 替代,Apache 2.0 可商用)
后端
excel6 小时前
前端加密的作用与使用场景
前端
行百里er6 小时前
Redis 版本演进、新特性与协议那些事儿
redis·后端