React 组件权限控制的实现
前话
具备用户体系的业务系统往往需要具备权限控制的能力。前后不分离的开发模式权限控制主要由后端结合模版引擎实现,而前后分离的开发模式由前后两端协商权限配置分别进行数据权限控制和组件权限控制。
正文
权限配置格式不限,为进行后续演示本篇设定权限配置如下:
export type IAuthConfig = {
keys: string[];
};
且提供一个Hook直接获取当前用户信息:
export type IUser = {
authConfig: IAuthConfig;
};
export function useUser() {
// ...略
return user;
}
首先我们先定义一个权限Hook,内容如下:
function getAuthKeys(auth: string | string[]) {
return Array.isArray(auth) ? auth : [auth];
}
export enum EAuthType {
"some" = "some",
"every" = "every",
}
export function useAuth() {
const { authConfig } = useUser();
// 权限标识列表
const authKeys = useMemo(() => authConfig?.keys ?? [], [authConfig]);
// 校验是否具备权限
const hasAuth = useCallback(
(auth: string | string[], type?: EAuthType) =>
getAuthKeys(auth)[type ?? EAuthType.every]((key) =>
authKeys.includes(key)
),
[authKeys]
);
const ret: [typeof authKeys, typeof hasAuth] = [authKeys, hasAuth];
return ret;
}
1. 控制方式
对于前端开发而言一般只需做组件控制,控制方式有很多种写法。
1.1 直接计算
const Cmpt: React.FC = () => {
const [, hasAuth] = useAuth();
// 计算是否有权限
const authorized = useMemo(() => hasAuth("test"), [hasAuth]);
// ...略
};
1.2 通用权限Hoc
export function withAuth<P extends Record<string, unknown> = {}>(
auth: string | string[],
type?: EAuthType
) {
return function (Component: any) {
const WrappedComponent: React.FC<P> = (props) => {
const [, hasAuth] = useAuth();
const instance = React.createElement(Component, {
...props,
});
// 计算是否有权限
const authorized = hasAuth(auth, type);
// ...略
};
WrappedComponent.displayName = `withAuth(${getDisplayName(Component)})`;
return WrappedComponent;
};
}
1.3 权限包裹组件
const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => {
const [, hasAuth] = useAuth();
// 计算是否有权限
const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]);
// ...略
};
2. 控制结果
常见控制结果为控制组件的显示或隐藏,或根据是否具备权限做组件的自定义渲染。
为方便演示后面统一使用权限包裹组件进行说明。
2.1 显隐控制
具备权限的组件直接渲染,否则返回null,即可实现显隐控制。权限包裹组件实现如下:
const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => {
const [, hasAuth] = useAuth();
// 计算是否有权限
const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]);
// 具备权限的组件直接渲染,否则返回null
return authorized ? children : null;
};
在需要权限控制的组件外包裹权限组件即可。
const Cmpt: React.FC = () => {
<>
<AuthWrapper auth="test">
<Button>Hello World</Button>
</AuthWrapper>
</>;
};
2.2 自定义渲染
实际业务场景中,存在需要提醒用户没有权限的情况,这时需要权限包裹组件支持自定义渲染,实现方式有多种:
- 静态props注入
- 动态props映射
- render props
相比较之下render props更为自由,权限包裹组件完善后实现如下:
type IProps = {
auth: string | string[];
type?: EAuthType;
};
const AuthWrapper: React.FC<IProps> = ({ auth, type, children }) => {
const [, hasAuth] = useAuth();
// 计算是否有权限
const authorized = useMemo(() => hasAuth(auth, type), [auth, type, hasAuth]);
// 自定义渲染
if (typeof children === "function") {
return children(authorized);
}
// 显隐控制
return authorized ? children : null;
};
这时就可以渲染提示无权限组件:
const Cmpt: React.FC = () => {
<>
<AuthWrapper auth="test">
{(authorized) => <Button disabled={!authorized}>Hello World</Button>}
</AuthWrapper>
</>;
};
3. 权限数据
前端开发做组件控制的颗粒度取决于权限数据的类型,权限数据类型分为静态权限和动态权限。
其中静态权限一般在完成登录认证后即可一次性获取,而动态权限则在操作数据时进行权限校验。
因此不难发现静态权限往往用于控制路由、页面或者粗糙的操作权限。而动态权限则能够对动态数据进行更细粒度的操作权限控制(需后端数据权限控制能力配合)。
3.1 静态权限
如前面描述,登录认证后即可从用户信息中得到权限标识,同样前面的栗子均也为静态权限示例。
3.2 动态权限
需要动态权限校验的场景在业务系统中也较为常见,例如用户能够看到列表数据,但禁止查阅无权限的数据详情。
由此可知对于用户而言,获取的动态的列表数据需要逐一进行权限校验,这时我们对权限Hook和包裹组件进行改造,改造后代码如下:
type IAuthDynamicConfig = {
// ...略
};
export function useAuth() {
// ...略
// 动态校验是否具有权限
const dynamicAuth = useCallback(
(auth: string | string[], type?: EAuthType, config?: IAuthDynamicConfig) =>
// 批量异步校验权限,实现略
append2DynamicAuthTask(auth, type, config),
[]
);
const ret: [typeof authKeys, typeof hasAuth, typeof dynamicAuth] = [
authKeys,
hasAuth,
dynamicAuth,
];
return ret;
}
const AuthWrapper: React.FC<IProps> = ({ auth, type, config, children }) => {
const [, hasAuth, dynamicAuth] = useAuth();
const [authorized, setAuthorized] = useState(false);
// 计算是否有权限
useEffect(() => {
if (config === undefined) {
setAuthorized(hasAuth(auth, type));
return;
}
dynamicAuth(auth, type, config).then(setAuthorized);
}, [auth, type, config, hasAuth, dynamicAuth]);
// ...略
};
使用方式如下:
const Cmpt: React.FC = () => {
// ...略
<>
{data.map((item) => (
<div key={item.id}>
<div>{item.title}</div>
<div>
<AuthWrapper
auth="detail"
config={{
module: "demo",
identify: item.id,
}}
>
{(authorized) => (
<Button disabled={!authorized} onClick={handleDetail}>
详情
</Button>
)}
</AuthWrapper>
</div>
</div>
))}
</>;
};
到此这篇关于React 组件权限控制的实现的文章就介绍到这了,更多相关React 组件权限控制内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341