这是一套完整的前端权限控制系统,基于 React + TypeScript + Zustand 技术栈构建。该系统实现了细粒度的权限控制,包括路由级权限、组件级权限和操作级权限,确保用户只能访问和操作其有权限的功能模块。具体代码请查看 Github Rustzen Admin Web。
核心架构
1. 权限数据结构
系统采用基于字符串的权限码设计,权限码格式为 module:resource:action
,例如:
system:user:create
- 用户创建权限system:user:edit
- 用户编辑权限system:user:delete
- 用户删除权限system:user:list
- 用户列表查看权限
typescript
interface UserInfoResponse {
id: number;
username: string;
realName?: string;
avatarUrl?: string;
permissions: string[]; // 权限码数组
isSystem: boolean; // 是否是系统管理员
}
2. 状态管理 - Zustand Store
权限状态通过 useAuthStore
进行集中管理,使用 Zustand 的 persist 中间件实现状态持久化:
typescript
interface AuthState {
userInfo: Auth.UserInfoResponse | null;
token: string | null;
updateUserInfo: (params: Auth.UserInfoResponse) => void;
updateToken: (params: string) => void;
setAuth: (params: Auth.LoginResponse) => void;
clearAuth: () => void;
checkPermissions: (code: string) => boolean;
checkMenuPermissions: (path: string) => boolean;
}
权限验证机制
1. 权限码验证算法
系统实现了智能的权限码匹配,支持通配符和层级权限,具体实现如下:
typescript
checkPermissions: (code: string) => {
const permissions = get().userInfo?.permissions || [];
if (permissions.length === 0) {
return false;
}
if (permissions.includes("*")) {
return true; // 超级管理员
}
if (permissions.includes(code)) {
return true; // 精确匹配
}
// 层级权限匹配:system:user:* -> system:*
const codeArr = code.split(":");
for (let i = codeArr.length - 1; i > 0; i--) {
const prefix = codeArr.slice(0, i).join(":") + ":*";
if (permissions.includes(prefix)) {
return true;
}
}
return false;
};
2. 路由权限验证
系统自动将路由路径转换为权限码进行验证:
typescript
const formatPathCode = (pathname: string) => {
const code = pathname.replace(/\//g, ":").slice(1);
// 创建页面
if (code.endsWith(":create")) {
return code;
}
// 编辑页面、详情页面
if (code.endsWith(":edit") || code.endsWith(":detail")) {
return code
.split(":")
.filter((s) => !/^\d+$/.test(s))
.join(":");
}
// 列表页面
return `${code}:list`;
};
权限组件体系
1. AuthGuard - 路由守卫
AuthGuard
组件负责路由级别的权限控制,在用户访问页面时进行权限验证:
typescript
export const AuthGuard: React.FC<AuthGuardProps> = ({ children }) => {
const location = useLocation();
const { token, updateUserInfo, checkMenuPermissions } = useAuthStore();
// 无 token 跳转登录页
if (!token) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
// 检查页面权限
const isPermission = checkMenuPermissions(location.pathname);
return isPermission ? children : <Navigate to="/403" replace />;
};
2. AuthWrap - 组件级权限控制
AuthWrap
组件用于控制组件的显示/隐藏:
typescript
export const AuthWrap: React.FC<AuthWrapProps> = ({
code,
children,
hidden = false,
}) => {
const isPermission = useAuthStore.getState().checkPermissions(code);
if (isPermission && !hidden) {
return children;
}
return null;
};
3. AuthConfirm - 操作级权限控制
AuthConfirm
组件用于需要确认的操作权限控制:
typescript
export const AuthConfirm: React.FC<AuthConfirmProps> = (props) => {
const handleConfirm = () => {
modalApi.confirm({
title: props.title,
content: props.description,
onOk: props.onConfirm,
onCancel: props.onCancel,
});
};
return (
<AuthWrap code={props.code} hidden={props.hidden}>
<span onClick={handleConfirm} className={props.className}>
{props.children}
</span>
</AuthWrap>
);
};
实际应用示例
1. 用户管理页面权限控制
typescript
export default function UserPage() {
return (
<ProTable<User.Item>
// ... 表格配置
toolBarRender={() => [
<AuthWrap code="system:user:create">
<UserModalForm mode="create" onSuccess={handleSuccess}>
<Button type="primary">Create User</Button>
</UserModalForm>
</AuthWrap>,
]}
columns={[
// ... 其他列
{
title: "Actions",
render: (_, entity) => (
<Space size="middle">
<AuthWrap code="system:user:detail">
<UserModalForm mode="detail" initialValues={entity}>
<a>Detail</a>
</UserModalForm>
</AuthWrap>
<MoreButton>
<AuthWrap code="system:user:edit">
<UserModalForm mode="edit" initialValues={entity}>
<a>Edit</a>
</UserModalForm>
</AuthWrap>
<AuthConfirm
code="system:user:delete"
title="Are you sure you want to delete this user?"
onConfirm={handleDelete}
>
Delete User
</AuthConfirm>
</MoreButton>
</Space>
),
},
]}
/>
);
}
2. 动态菜单生成
系统根据用户权限动态生成菜单:
typescript
export const getMenuData = (): AppRouter[] => {
const { checkMenuPermissions } = useAuthStore.getState();
const getMenuList = (menuList: AppRouter[]): AppRouter[] => {
return menuList
.filter((item) => {
if (!item.path) return false;
if (item.children) return true;
return checkMenuPermissions(item.path);
})
.map((item) => ({
...item,
children: item.children ? getMenuList(item.children) : undefined,
}))
.filter((item) => {
// 隐藏无子菜单的项目
if (item.children?.length === 0) {
return false;
}
return true;
});
};
return getMenuList(pageRoutes);
};
权限系统特点
1. 细粒度控制
- 路由级权限:控制页面访问
- 组件级权限:控制功能模块显示
- 操作级权限:控制具体操作按钮
2. 智能权限匹配
- 支持精确权限码匹配
- 支持通配符权限(
*
) - 支持层级权限继承(
system:user:*
→system:*
)
3. 用户体验优化
- 无权限内容自动隐藏,避免界面混乱
- 权限验证失败时优雅降级
- 支持权限状态持久化
4. 权限码命名规范
- 使用
module:resource:action
格式 - 保持命名一致性
- 避免过于复杂的权限层级
5. 组件使用建议
场景 | 组件 | 示例 |
---|---|---|
页面访问控制 | AuthGuard |
路由级权限验证 |
功能模块显示 | AuthWrap |
按钮、表单等组件 |
危险操作确认 | AuthConfirm |
删除、导出等操作 |
🎯 总结:简单的权限控制方案
核心收益
通过这套权限系统,我实现了:
- 开发效率提升:声明式权限控制,减少重复代码
- 用户体验优化:无权限内容自动隐藏,界面更清爽
- 维护成本降低:权限逻辑集中管理,变更更简单
- 安全性增强:前后端权限双重验证
技术亮点
- 智能权限匹配:支持精确匹配、通配符匹配、层级继承
- 组件化设计:三个核心组件覆盖所有权限场景
- TypeScript 支持:完整的类型定义,开发体验优秀
- 性能优化:权限验证结果缓存,避免重复计算
适用场景
- 中小型管理系统:权限结构相对简单
- 快速开发项目:需要快速构建权限控制
- 用户体验优先:注重界面美观和交互流畅
- 前后端分离:基于 API 的权限数据交互
🧭 写在最后
这套权限系统是我在开发 rustzen-admin 过程中的真实实践,从最初的直接写死权限到最终的简单设计。在目前项目阶段,简单的权限控制已经足够满足需求。前后端统一的权限规范,可以避免权限数据不一致的问题,也方便后续的权限扩展。 如果你也在为权限控制而烦恼,不妨试试这套方案。也许它会像帮助我一样,让你的权限系统变得更加简单和易维护。
📫 相关资源:
- 后端权限系统设计
- GitHub 源码
- 我的博客:idaibin.dev