Ant Design Pro路由守卫实现:页面访问控制与权限校验
引言:你还在为路由权限控制头痛吗?
在现代前端应用开发中,路由守卫(Route Guard)是保障应用安全的关键组件。你是否遇到过以下痛点:
- 未登录用户直接访问需要认证的页面
- 普通用户越权访问管理员专属功能
- 页面跳转时权限校验逻辑混乱
- 多角色权限管理代码臃肿难以维护
本文将系统讲解Ant Design Pro(以下简称"Pro")中路由守卫的实现方案,通过"认证守卫→权限守卫→动态路由"的三层架构,构建完整的页面访问控制体系。读完本文你将掌握:
- Pro项目中路由守卫的工作原理
- 基于Umi Max的认证状态管理
- 细粒度的RBAC权限模型实现
- 动态路由与菜单生成的最佳实践
- 权限异常处理与用户体验优化
一、路由守卫核心概念与工作流程
1.1 什么是路由守卫?
路由守卫(Route Guard)是前端路由系统中的访问控制机制,在用户导航到目标路由前进行权限校验,决定是否允许访问、重定向或拒绝请求。它类似于后端的中间件(Middleware),但运行在浏览器环境中。
1.2 路由守卫的分类
Pro项目中主要使用三种守卫类型:
| 守卫类型 | 触发时机 | 典型应用场景 |
|---|---|---|
| 全局守卫 | 每次路由切换 | 登录状态验证、Token过期检查 |
| 路由独享守卫 | 特定路由配置 | 角色权限校验、操作权限检查 |
| 组件内守卫 | 组件生命周期 | 表单未保存提示、数据加载状态 |
1.3 工作流程图解
二、Ant Design Pro路由守卫实现基础
2.1 项目路由配置结构
Pro项目的路由配置通常在config/routes.ts中定义,典型结构如下:
// config/routes.ts 示例
export default [
{
path: '/',
component: '../layouts/MainLayout',
routes: [
{ path: '/user/login', component: './user/login', title: '登录' },
{
path: '/dashboard',
component: './dashboard',
name: '仪表盘',
access: 'canReadDashboard' // 权限标识
},
{
path: '/admin',
component: './Admin',
name: '管理后台',
access: 'canAdmin', // 管理员权限
routes: [
{ path: '/admin/users', component: './admin/users', access: 'canManageUser' }
]
}
]
}
];
2.2 Umi Max路由系统核心API
Pro基于Umi Max构建,提供了以下关键路由API:
| API | 作用 | 应用场景 |
|---|---|---|
history.push() | 编程式导航 | 登录后重定向 |
getInitialState() | 全局状态初始化 | 用户信息加载 |
layout | 布局配置 | 全局权限检查 |
access | 权限定义 | 细粒度权限控制 |
三、全局认证守卫:登录状态控制
3.1 基于getInitialState的用户状态管理
src/app.tsx中的getInitialState函数是Pro项目的初始化入口,负责加载用户信息并决定路由访问权限:
// src/app.tsx
export async function getInitialState(): Promise<{
currentUser?: API.CurrentUser;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
const fetchUserInfo = async () => {
try {
// 调用用户信息接口
const response = await queryCurrentUser({ skipErrorHandler: true });
return response.data;
} catch (error) {
// 未登录或Token失效,重定向到登录页
history.push('/user/login');
}
return undefined;
};
// 非登录页面才执行用户信息加载
const { location } = history;
if (!['/user/login', '/user/register'].includes(location.pathname)) {
const currentUser = await fetchUserInfo();
return { currentUser, fetchUserInfo };
}
return { fetchUserInfo };
}
3.2 布局中的路由守卫实现
Pro的布局配置中提供了onPageChange钩子,用于在页面切换时进行全局权限检查:
// src/app.tsx
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
return {
onPageChange: () => {
const { location } = history;
// 未登录用户访问需要认证的页面时重定向
if (!initialState?.currentUser && location.pathname !== '/user/login') {
history.push('/user/login');
}
},
// 其他布局配置...
};
};
3.3 登录状态检查流程
四、基于Access的权限守卫实现
4.1 Access权限定义文件
src/access.ts是Pro项目权限控制的核心,定义了应用中所有权限检查逻辑:
// src/access.ts
export default function access(initialState: { currentUser?: API.CurrentUser }) {
const { currentUser } = initialState || {};
return {
// 管理员权限
canAdmin: currentUser && currentUser.access === 'admin',
// 编辑权限
canEdit: currentUser && ['admin', 'editor'].includes(currentUser.access!),
// 只读权限
canRead: currentUser && ['admin', 'editor', 'viewer'].includes(currentUser.access!),
// 自定义数据权限检查
canAccessProject: (projectId: number) => {
return currentUser?.projects?.includes(projectId) || false;
}
};
}
4.2 路由级别权限控制
在路由配置中通过access属性关联权限定义:
// config/routes.ts
export default [
{
path: '/admin',
component: './Admin',
name: '管理后台',
access: 'canAdmin', // 只允许管理员访问
routes: [
{
path: '/admin/settings',
component: './admin/settings',
access: 'canEdit' // 需要编辑权限
}
]
},
{
path: '/reports',
component: './reports',
name: '报表中心',
access: 'canRead' // 需要只读权限
}
];
4.3 组件级别权限控制
使用Pro提供的Access组件实现组件内部的权限控制:
// 组件中使用Access控制渲染
import { Access } from 'umi';
const ProjectPage = ({ projectId }) => {
return (
<div>
<h1>项目详情</h1>
{/* 基于角色的权限控制 */}
<Access accessible={access.canEdit}>
<Button type="primary">编辑项目</Button>
</Access>
{/* 基于数据的权限控制 */}
<Access accessible={access.canAccessProject(projectId)}>
<Button>管理成员</Button>
</Access>
{/* 无权限时的降级显示 */}
<Access
accessible={access.canAdmin}
fallback={<div>需要管理员权限</div>}
>
<Button danger>删除项目</Button>
</Access>
</div>
);
};
4.4 权限矩阵设计
复杂应用推荐使用权限矩阵统一管理:
// src/constants/permissionMatrix.ts
export const PERMISSION_MATRIX = {
// 模块: { 角色: 权限 }
dashboard: {
viewer: ['read'],
editor: ['read', 'create', 'update'],
admin: ['read', 'create', 'update', 'delete']
},
users: {
viewer: ['read'],
editor: ['read', 'create'],
admin: ['read', 'create', 'update', 'delete']
}
};
// 在access.ts中使用
import { PERMISSION_MATRIX } from './constants/permissionMatrix';
export default function access(initialState) {
const { currentUser } = initialState || {};
const role = currentUser?.access || 'guest';
return {
// 动态生成权限检查函数
hasPermission: (module, action) => {
return PERMISSION_MATRIX[module]?.[role]?.includes(action) || false;
}
};
}
五、动态路由与菜单生成
5.1 基于后端权限的动态路由
对于大型应用,通常需要根据用户角色动态生成路由:
// src/app.tsx
export async function getInitialState() {
// 1. 获取用户信息
const currentUser = await fetchUserInfo();
// 2. 获取用户权限路由
let routes = [];
if (currentUser) {
try {
const response = await queryUserRoutes();
routes = response.data;
} catch (error) {
console.error('获取动态路由失败', error);
}
}
return {
currentUser,
routes
};
}
// 配置动态路由
export function patchRoutes({ routes }) {
const { initialState } = window.g_app._store.getState();
// 合并静态路由与动态路由
routes[0].routes = [...routes[0].routes, ...initialState.routes];
}
5.2 动态菜单生成与权限过滤
Pro的菜单会自动根据路由配置和权限过滤生成:
// src/layouts/MainLayout.tsx
import { Menu } from '@ant-design/pro-components';
import { useAccess, useIntl } from 'umi';
const MainLayout = ({ children }) => {
const { initialState } = useModel('@@initialState');
const access = useAccess();
const intl = useIntl();
return (
<ProLayout
menuHeaderRender={false}
// 自定义菜单生成逻辑
menuItemRender={(item, dom) => {
// 过滤无权限的菜单项
if (item.access && !access[item.access]) {
return null;
}
return dom;
}}
>
{children}
</ProLayout>
);
};
5.3 动态路由加载流程图
六、高级应用:复杂场景处理
6.1 路由参数权限校验
某些场景需要根据路由参数进行权限校验:
// src/pages/projects/$id.tsx
import { useAccess, useParams, history } from 'umi';
import { useEffect } from 'react';
const ProjectDetail = () => {
const { id } = useParams();
const access = useAccess();
useEffect(() => {
// 检查是否有权限访问该项目
if (!access.canAccessProject(Number(id))) {
history.push('/403');
}
}, [id, access]);
return <div>项目详情 {id}</div>;
};
6.2 基于角色的动态重定向
根据用户角色重定向到不同页面:
// src/app.tsx
export function render(oldRender) {
// 等待初始状态加载完成
window.g_app
.isReady()
.then(() => {
const { initialState } = window.g_app._store.getState();
const { currentUser, location } = initialState;
// 已登录且在登录页,根据角色重定向
if (currentUser && location.pathname === '/user/login') {
if (currentUser.access === 'admin') {
history.push('/admin');
} else {
history.push('/dashboard');
}
}
oldRender();
})
.catch(oldRender);
}
6.3 权限变更时的路由刷新
用户权限变更后刷新路由:
// src/components/ChangeRoleButton.tsx
import { Button } from 'antd';
import { useAccess, useModel } from 'umi';
const ChangeRoleButton = () => {
const { initialState, setInitialState } = useModel('@@initialState');
const access = useAccess();
const handleChangeRole = async (newRole) => {
// 调用API切换角色
await updateUserRole(newRole);
// 重新获取用户信息和权限
const newUser = await initialState.fetchUserInfo();
setInitialState({ ...initialState, currentUser: newUser });
// 刷新当前页面路由
history.go(0);
};
return (
<Button onClick={() => handleChangeRole('editor')}>
切换为编辑角色
</Button>
);
};
七、最佳实践与性能优化
7.1 权限守卫实现最佳实践
-
权限集中管理
- 将所有权限定义集中在
access.ts中 - 使用常量定义权限标识,避免硬编码
- 将所有权限定义集中在
-
路由守卫分层实现
- 全局守卫:处理登录状态
- 路由守卫:处理角色权限
- 组件守卫:处理数据权限
-
权限预加载策略
- 关键权限在
getInitialState中预加载 - 非关键权限按需加载
- 关键权限在
7.2 性能优化策略
| 优化点 | 实现方案 | 性能提升 |
|---|---|---|
| 路由懒加载 | 使用Umi的动态导入 | 初始加载时间减少40%+ |
| 权限缓存 | 将权限结果缓存到内存 | 减少重复计算90%+ |
| 路由预加载 | 预加载常用路由组件 | 页面切换速度提升60%+ |
| 菜单渲染优化 | 使用虚拟滚动渲染长菜单 | 菜单渲染时间减少70%+ |
7.3 常见问题解决方案
| 问题 | 解决方案 | 代码示例 |
|---|---|---|
| 路由死循环 | 增加重定向检查 | if (location.pathname !== loginPath) history.push(loginPath) |
| 权限闪烁 | 使用骨架屏或加载状态 | <Spin spinning={!initialState?.loaded}> |
| Token过期处理 | 实现请求拦截器刷新Token | request.interceptors.response.use(handleTokenExpire) |
| 403页面优化 | 提供权限申请入口 | <Button onClick={applyPermission}>申请权限</Button> |
八、总结与展望
8.1 核心知识点回顾
本文详细介绍了Ant Design Pro中路由守卫的实现方案,包括:
- 全局认证守卫:基于
getInitialState和layout配置实现登录状态控制 - 细粒度权限控制:使用
access.ts定义权限规则,在路由和组件中应用 - 动态路由管理:根据用户权限动态生成和过滤路由
- 复杂场景处理:路由参数校验、动态重定向和权限变更
8.2 进阶学习路径
要深入掌握前端权限控制,建议进一步学习:
- CASL、RBAC等权限模型理论
- Umi Max插件开发(自定义路由守卫插件)
- 基于React Context的权限状态管理
- 前端权限可视化配置工具开发
8.3 开源推荐
- umi-access:Umi官方权限插件
- casl-react:灵活的React权限控制库
- react-route-guard:轻量级路由守卫组件
通过合理设计的路由守卫系统,不仅能有效保障应用安全,还能提升用户体验和开发效率。希望本文介绍的方案能帮助你构建更安全、更灵活的Ant Design Pro应用。
如果你觉得本文有帮助,请点赞、收藏并关注作者,后续将带来更多Ant Design Pro实战技巧!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



