ICE飞冰权限管理

前言

前端页面权限在日常开发中,主要有以下:

  • 登录授权,用户没有登录只能访问登录页面,如果处于登录状态则跳转到当前用户的默认首页;

  • 路由授权,当前登录用户的角色,如果对一个 URL 没有权限访问,则跳转到 403 页面;

  • 数据授权,当访问一个没有权限的 API,则跳转到 403 页面;

  • 操作授权,当页面中某个按钮或者区域没有权限访问则在页面中隐藏

本文主要针对ICE飞冰模板,介绍下如何开发权限管理,主要是使用,不涉及任何架构设计及原理,关于原理可以参考源码分析,或者参考本文参考链接。

实践

首先先安装依赖,因为飞冰权限是使用Authorized 权限组件实现了基本的权限管理方案。

1
2
npm install antd --save
npm install ant-design-pro@latest --save

因为ant-design-pro依赖antd,因此需要首先安装antd

在开始之前需要新增两个util,一个设置登录权限,一个为了重新渲染权限

authority.js

1
2
3
4
5
6
7
8
9
10
11
12
13
// use localStorage to store the authority info, which might be sent from server in actual project.
export function getAuthority() {
return localStorage.getItem('ice-pro-authority') || 'admin';
}

export function setAuthority(authority) {
return localStorage.setItem('ice-pro-authority', authority);
}

export function removeAuthority() {
return localStorage.removeItem('ice-pro-authority');
}

Authorized.js

1
2
3
4
5
6
7
8
9
10
11
12
13
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
import { getAuthority } from './authority';

let Authorized = RenderAuthorized(getAuthority()); // eslint-disable-line

// 更新权限
const reloadAuthorized = () => {
Authorized = RenderAuthorized(getAuthority());
};

export { reloadAuthorized };
export default Authorized;

登录授权

在登录页面,引入依赖,然后再login方法处设置权限,如下:

1
2
3
4
5
6
7
import { setAuthority } from '../../utils/authority';
import { reloadAuthorized } from '../../utils/Authorized';

// 这里设置权限为admin,添加该方法到login验证通过的方法处。
setAuthority('admin');
//重新渲染页面
reloadAuthorized();

退出

1
removeAuthority()

路由授权

编辑routerConfig.js,在需要设置权限的地方添加authority属性,值可以是string,数组。例如:

1
2
3
4
5
{
path: '/approve/tasks',
component: ApproveTask,
authority: 'admin',
}

在MainRoutes.jsx 设置重定向地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import {AuthorizedRoute} from 'ant-design-pro/lib/Authorized';
/**
* 根据菜单取得重定向地址.
*/
getRedirectData = () => {
const redirectData = [];
const getRedirect = (item) => {
if (item && item.children) {
if (item.children[0] && item.children[0].path) {
redirectData.push({
from: `${item.path}`,
to: `${item.children[0].path}`,
});
item.children.forEach((children) => {
getRedirect(children);
});
}
}
};

asideMenuConfig.forEach(getRedirect);

return redirectData;
};

/**
* 渲染权限路由组件
*/
renderAuthorizedRoute = (item, index) => {
return item.component ? (
<AuthorizedRoute
key={index}
path={item.path}
component={item.component}
exact={item.exact}
authority={item.authority}
redirectPath="/exception/403"
/>
) : null;
};

render() {
const redirectData = this.getRedirectData();
return (
<Switch>
{/* 渲染权限路由表 */}

{routerData.map(this.renderAuthorizedRoute)}

{/* 路由重定向,嵌套路由默认重定向到当前菜单的第一个路由 */}

{redirectData.map((item, index) => {
return <Redirect key={index} exact from={item.from} to={item.to} />;
})}

{/* 根路由默认重定向到 /dashboard */}
<Redirect from="/" to="/user/login" />

{/* 未匹配到的路由重定向到 NotFound */}
<Route component={NotFound} />
</Switch>
);
}
}

这里跳转到403页面,这里不再说明如何增加403页面

菜单授权

修改meConfig.js。在需要添加权限地方,设置角色名称,authority: 'admin',子菜单会继承父菜单的权限。详细案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const asideMenuConfig = [
{
name: 'Topics',
path: '/topics',
icon: 'home2',
children: [
{ name: 'Topic List', path: '/topics/list', authority: ['admin', 'member'] },
{ name: 'My Task', path: '/topics/task', authority: ['admin', 'member'] },
],
},
{
name: 'Approve',
path: '/approve',
icon: 'publish',
authority: 'admin',
children: [
{ name: 'Task List', path: '/approve/tasks' },
],
},
];

\src\layouts\BasicLayout\components\Aside页面添加权限校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
import { getAuthority } from '../../../../utils/authority';
import { asideMenuConfig } from '../../../../menuConfig';

//省略部分方法
/**
* 权限检查
*/
checkPermissionItem = (authority, ItemDom) => {
if (Authorized.check) {
const { check } = Authorized;
return check(authority, ItemDom);
}

return ItemDom;
};


/**
* 获取菜单项数据
*/
getNavMenuItems = (menusData) => {
if (!menusData) {
return [];
}

return menusData
.filter(item => item.name && !item.hideInMenu)
.map((item, index) => {
const ItemDom = this.getSubMenuOrItem(item, index);
return this.checkPermissionItem(item.authority, ItemDom);
})
.filter(item => item);
};


/**
* 二级导航
*/
getSubMenuOrItem = (item, index) => {
if (item.children && item.children.some(child => child.name)) {
const childrenItems = this.getNavMenuItems(item.children);

if (childrenItems && childrenItems.length > 0) {
return (
<SubNav
key={index}
label={
<span>
{item.icon ? (
<FoundationSymbol size="small" type={item.icon} />
) : null}
<span className="ice-menu-collapse-hide">
{item.name}
</span>
</span>
}
>
{childrenItems}
</SubNav>
);
}
return null;
}

const linkProps = {};
if (item.newWindow) {
linkProps.href = item.path;
linkProps.target = '_blank';
} else if (item.external) {
linkProps.href = item.path;
} else {
linkProps.to = item.path;
}
return (
<Item key={item.path}>
<Link {...linkProps}>
<span>
{item.icon ? (
<FoundationSymbol size="small" type={item.icon} />
) : null}
<span className="ice-menu-collapse-hide">{item.name}</span>
</span>
</Link>
</Item>
);
};

render() {
const { location } = this.props;
const { pathname } = location;

return (
<Layout.Aside width="240" theme="light" className="custom-aside">
<Nav
defaultSelectedKeys={[pathname]}
selectedKeys={[pathname]}
onOpen={this.onOpenChange}
openKeys={this.state.openKeys}
className="custom-menu"
>
{this.getNavMenuItems(asideMenuConfig)}
</Nav>
{/* 侧边菜单项 end */}
</Layout.Aside>
);
}

操作授权

这里说的数据操作授权,即根据不同权限在页面显示不同的菜单,这里是实现只有管理员角色才能操作的按钮。

1
2
3
4
5
6
7
8
9
10
11
12
import RenderAuthorized from 'ant-design-pro/lib/Authorized';
import { getAuthority } from '../../utils/authority';
const Authorized = RenderAuthorized(getAuthority());

ReactDOM.render(
<div>
<Authorized authority="admin" noMatch={noMatch}>
按钮....
</Authorized>
</div>,
mountNode,
);

参考

  1. 权限管理
  2. Ant Design Pro权限(Authorized)管理模块深入解读