2022-07-01 16:47:25 +08:00
|
|
|
---
|
2022-07-07 17:13:41 +08:00
|
|
|
title: 权限管理
|
2023-03-17 11:03:19 +08:00
|
|
|
order: 0510
|
2022-07-01 16:47:25 +08:00
|
|
|
---
|
2023-03-17 11:03:19 +08:00
|
|
|
> 对于移动端场景,鉴权通常由 SSO 或统一登录平台完成,业务开发者无需关注。本文档主要介绍中后台场景中的权限管理。
|
2022-07-01 16:47:25 +08:00
|
|
|
|
2022-10-19 15:20:02 +08:00
|
|
|
import Tabs from '@theme/Tabs';
|
|
|
|
import TabItem from '@theme/TabItem';
|
|
|
|
|
|
|
|
<details open>
|
|
|
|
<summary>示例</summary>
|
|
|
|
<ul>
|
|
|
|
<li>
|
2022-11-24 13:24:35 +08:00
|
|
|
<a href="https://github.com/alibaba/ice/tree/master/examples/with-auth" target="_blank" rel="noopener noreferrer">
|
2022-11-07 13:53:18 +08:00
|
|
|
with-auth
|
2022-10-19 15:20:02 +08:00
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</details>
|
|
|
|
|
2023-03-17 11:03:19 +08:00
|
|
|
中后台 Web 应用中,权限管理是经常会涉及的需求之一,通常包含以下几种常见的权限管理类型:
|
2022-10-19 15:20:02 +08:00
|
|
|
|
|
|
|
- 页面权限:当用户访问某个没有权限的页面时跳转到无权限页面
|
|
|
|
- 操作权限:页面中的某些按钮或组件针对无权限的用户直接隐藏
|
|
|
|
- 接口权限:当用户通过操作调用没有权限的接口时跳转到无权限页面
|
|
|
|
|
|
|
|
ice.js 提供 `@ice/plugin-auth` 插件,帮助用户更简单管理前两种类型的权限。接口权限管理请见数据请求文档。
|
|
|
|
|
|
|
|
## 安装插件
|
|
|
|
|
|
|
|
安装插件:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
$ npm i @ice/plugin-auth -D
|
|
|
|
```
|
|
|
|
|
|
|
|
在 `ice.config.mts` 中添加插件:
|
|
|
|
|
|
|
|
```ts title="ice.config.mts"
|
|
|
|
import { defineConfig } from '@ice/app';
|
|
|
|
import auth from '@ice/plugin-auth';
|
|
|
|
|
2022-11-11 11:02:51 +08:00
|
|
|
export default defineConfig(() => ({
|
2022-10-19 15:20:02 +08:00
|
|
|
plugins: [
|
|
|
|
auth(),
|
|
|
|
],
|
2022-11-11 11:02:51 +08:00
|
|
|
}));
|
2022-10-19 15:20:02 +08:00
|
|
|
```
|
|
|
|
|
|
|
|
## 初始化权限数据
|
|
|
|
|
|
|
|
大多数情况下权限管理通常需要从服务端获取权限数据,然后在前端通过权限对比以此控制页面、操作等等权限行为。约定在 `src/app.ts` 中导出 `auth` 对象,该对象包含从服务端异步获取初始化的权限数据,并且约定最终返回格式为 `{ initialAuth: { [key: string]: boolean } }`。
|
|
|
|
|
|
|
|
```ts title="src/app.ts"
|
2023-02-28 15:08:10 +08:00
|
|
|
import { defineAuthConfig } from '@ice/plugin-auth/types';
|
2022-10-19 15:20:02 +08:00
|
|
|
|
2022-11-11 11:02:51 +08:00
|
|
|
export const authConfig = defineAuthConfig(async () => {
|
2022-10-19 15:20:02 +08:00
|
|
|
// 模拟请求权限数据
|
|
|
|
// const data = (await fetch('/api/auth')).json();
|
|
|
|
return {
|
|
|
|
initialAuth: {
|
|
|
|
admin: true,
|
|
|
|
guest: false,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
## 页面权限
|
|
|
|
|
2022-11-11 11:02:51 +08:00
|
|
|
如需对某些页面进行权限控制,只需在页面组件的 `pageConfig` 中配置准入权限即可。
|
2022-10-19 15:20:02 +08:00
|
|
|
|
|
|
|
<Tabs>
|
|
|
|
<TabItem value="home" label="src/pages/index.tsx">
|
|
|
|
|
|
|
|
```tsx
|
2022-11-11 11:02:51 +08:00
|
|
|
import { definePageConfig } from 'ice';
|
|
|
|
|
2022-10-19 15:20:02 +08:00
|
|
|
export default function Home() {
|
|
|
|
return <div>Home</div>
|
|
|
|
}
|
|
|
|
|
2022-11-11 11:02:51 +08:00
|
|
|
export const pageConfig = definePageConfig(() => ({
|
|
|
|
// 当前用户是 admin 时,有权限访问该页面
|
|
|
|
auth: ['admin'],
|
|
|
|
}));
|
2022-10-19 15:20:02 +08:00
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="user" label="src/pages/about.tsx">
|
|
|
|
|
|
|
|
```tsx
|
2022-11-11 11:02:51 +08:00
|
|
|
import { definePageConfig } from 'ice';
|
|
|
|
|
2022-10-19 15:20:02 +08:00
|
|
|
export default function About() {
|
|
|
|
return <div>About</div>
|
|
|
|
}
|
|
|
|
|
2022-11-11 11:02:51 +08:00
|
|
|
export const pageConfig = definePageConfig(() => ({
|
|
|
|
// 当前用户是 guest 时,无权限访问该页面
|
|
|
|
auth: ['guest'],
|
|
|
|
}));
|
2022-10-19 15:20:02 +08:00
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
</Tabs>
|
|
|
|
|
|
|
|
## 操作权限
|
|
|
|
|
|
|
|
在某些场景下,如某个组件中要根据角色判断是否有操作权限,我们可以通过 useAuth Hooks 在组件中获取权限数据,同时也可以更新初始的权限数据。
|
|
|
|
|
|
|
|
### 获取权限数据
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
import React from 'react';
|
|
|
|
import { useAuth } from 'ice';
|
|
|
|
|
|
|
|
function Foo() {
|
|
|
|
const [auth] = useAuth();
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
当前用户权限数据:
|
|
|
|
<code>{JSON.stringify(auth)}</code>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### 设置权限数据
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
import React from 'react';
|
|
|
|
import { useAuth } from 'ice';
|
|
|
|
|
|
|
|
function Home() {
|
|
|
|
const [auth, setAuth] = useAuth();
|
|
|
|
|
|
|
|
// 更新权限,与默认的 auth 数据进行合并
|
|
|
|
function updateAuth() {
|
|
|
|
setAuth({ admin: false, guest: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
当前用户角色:
|
|
|
|
<code>{JSON.stringify(auth)}</code>
|
|
|
|
<button type="button" onClick={updateAuth}>
|
|
|
|
更新权限
|
|
|
|
</button>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### 自定义权限组件
|
|
|
|
|
|
|
|
对于操作类权限,通常我们可以自定义封装权限组件,以便更细粒度的控制权限和复用。
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
import React from 'react';
|
|
|
|
import { useAuth } from 'ice';
|
|
|
|
import NoAuth from '@/components/NoAuth';
|
|
|
|
|
|
|
|
function Auth({ children, authKey, fallback }) {
|
|
|
|
const [auth] = useAuth();
|
|
|
|
// 判断是否有权限
|
|
|
|
const hasAuth = auth[authKey];
|
|
|
|
|
|
|
|
// 有权限时直接渲染内容
|
|
|
|
if (hasAuth) {
|
|
|
|
return children;
|
|
|
|
} else {
|
|
|
|
// 无权限时显示指定 UI
|
|
|
|
return fallback || NoAuth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Auth;
|
|
|
|
```
|
|
|
|
|
|
|
|
使用如下:
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
function Foo() {
|
|
|
|
return (
|
|
|
|
<Auth authKey={'starRepo'}>
|
|
|
|
<Button type="button">Star</Button>
|
|
|
|
</Auth>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## 自定义 Fallback
|
|
|
|
|
|
|
|
支持自定义无权限时的展示组件,默认为 `<>No Auth</>`
|
|
|
|
|
|
|
|
```diff title="src/app.tsx"
|
2023-02-28 15:08:10 +08:00
|
|
|
import { defineAuthConfig } from '@ice/plugin-auth/types';
|
2022-10-19 15:20:02 +08:00
|
|
|
|
2022-11-11 11:02:51 +08:00
|
|
|
export const authConfig = defineAuthConfig(async () => {
|
2022-10-19 15:20:02 +08:00
|
|
|
return {
|
|
|
|
initialAuth: {
|
|
|
|
admin: true,
|
|
|
|
},
|
|
|
|
+ NoAuthFallback: (routeConfig) => {
|
|
|
|
+ console.log(routeConfig); // 当前页面的配置
|
|
|
|
+ return (
|
|
|
|
+ <div>没有权限</div>
|
2022-11-11 11:02:51 +08:00
|
|
|
+ );
|
2022-10-19 15:20:02 +08:00
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
});
|
|
|
|
```
|