umimax + Nest.js 实现权限管理系统
前端
在 umi 文档中,我们可以看到有一个称为权限配置的功能,它提供了一套自己的权限管理方案。
权限定义
我们约定将 src/access.ts
作为权限定义文件,在其中可以定义和判断用户是否具有某个权限。
路由权限
通过在路由配置中添加 access
字段,可以判断路由是否有权限访问。
功能权限
使用 useAccess
来获取权限,可以获得所有定义的权限。
获取用户权限信息时机
我们约定在 src/app.tsx
中导出一个 getInitialState
方法,用于初始化信息。例如:
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,
};
}
上述代码中,我们在初始化时判断是否为登录页,从而获取存储的信息(权限)。但是,如果登录信息发生改变,则需要重新获取最新的登录信息。因此,在登录时,我们需要重新初始化:
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
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
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
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;
}
定义好三个表后,基本完成。
在查询用户信息时,我们也需要查询用户的角色和权限:
this.userRepository.findOne({
where: { id, state: true },
select: ['name', 'avator', 'id', 'mobile'],
relations: ['roles', 'roles.permissions'],
});
更新用户角色:
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。