传送门
前言
-
项目中的权限问题在面试中非常高频,无论是vue还是react,问的你一脸闷逼,千奇百怪,其实按照你项目中答就好,给你出业务场景对应给我你自己的方案就好。没那么玄乎。
-
权限和业务关联非常大,应用场景也比较广泛,pc,app等等,运用的也比较广泛,你现在在任何商业网站都能看到权限的区分。
-
关于权限的问题,大家也别想太复杂,要根据自己的实际业务出发,去实现登陆权限,路由权限,按钮级别权限
-
本篇教程更多的是给大家提供一个权限的一个思路,如果遇到更加复杂的业务场景对应梳理好然后做出对应的实现就好也可以找我来交流链接。
权限应用场景
- 用户已登陆权限,token怎么放,一般是放在headers里面,后端怎么处理token的过期时间等等,这块就不是我们所关心的,和他们设计有关,我们只需要处理好我们自己 的业务逻辑就好,是放在浏览器哪个位置,是否需要长期存在,还是浏览器关闭之后就消失,等等。这些都是要和产品方沟通处理的
- 用户未登陆的权限是哪些,表现形式是哪些,可以使用项目中哪些功能等等,触发需要权限的功能时是让弹框提示用户请登录,还是直接跳转到登陆页面,还是弹出一个登陆框让用户直接登陆。
- 路由权限 路由是否是动态路由,由配置页面生成,路由数据由后端返回,前端动态渲染路由,还是前端写死路由,通过flag判断路由是否展示,其次是如果访问到没有权限的路由的时候下一步是做什么, 是直接跳转到登陆页,移除token,还是展示一个提示的页面,提示用户没有此权限等等的展示效果,都是暗战你具体的业务场景去做处理的,不同的产品思路去展示就好
const PrivateRoute: FC<RouteProps> = (props) => {
const logged = sessionStorage.getItem("token");
const history = useHistory();
// 判断是否有token,如果有就展示,如果没有就不展示路由,展示403页面
return logged ? (
<Route {...props} />
) : (
<Result
status="403"
title="403"
subTitle={"无权限"}
extra={
<Button type="primary" onClick={() => history.push("/login")}>
跳转到登陆
</Button>
}
/>
);
};
- 角色权限
- 模块权限有超级管理员,普通管理员,非管理员,访客,等等,每一个角色对应何种权限,有何种功能,以及如何展示,这个和产品业务对接好,对应展示不同权限信息就好。
- 组件权限
- 组件权限在业务中极少使用,基本和按钮权限类似,是否直接隐藏。还是要看你们项目的具体业务,再去做处理
- 按钮权限
- 按钮也是一个组件按钮权限基本上是就比较简单了,就是展示不展示,或者说按钮展示,请求接口时,厚度去做判断,该操作是否有权限。
- 下面是我们这个项目的权限处理
/**
* 按钮权限判定
*/
function AuthorizedButton({
children,
authority,
render,
noMatch,
}: AuthorizedProps) {
// 目前是写死的按钮权限,真实场景应该会有配置页面,通过接口返回对应权限,然后放到permission中
const [permissions] = useState<string[]>(["button", "button1", "button2"]);
const result = checkPermissions(
authority,
permissions,
render ? render() : children,
noMatch
);
return <>{result}</>;
}
-
核心逻辑
export type IAuthorityType =
| undefined
| string
| string[]
| Promise<boolean>
| ((permissions: string[]) => boolean | Promise<boolean>);
/**
* 通用权限检查方法
* @param authority - 按钮权限判定
* @param permissions - 当前权限
* @param target - 通过的组件
* @param Exception - 未通过的组件
*/
const checkPermissions = <T, K>(
authority: IAuthorityType,
permissions: string[] = [],
target: T,
Exception: K
): T | K | React.ReactNode => {
// 没有判定权限.默认查看所有
if (!authority) {
return target;
}
// 数组处理
if (Array.isArray(authority)) {
if (permissions.some((item) => authority.includes(item))) {
return target;
}
if (
permissions.length > 0 &&
permissions.every((item) => authority.includes(item))
) {
return target;
}
return Exception;
}
// string 处理
if (typeof authority === "string") {
if (permissions.some((item) => authority === item)) {
return target;
}
return Exception;
}
// Promise 处理
if (authority instanceof Promise) {
return (
<PromiseRender<T, K> ok={target} error={Exception} promise={authority} />
);
}
// Function 处理
if (typeof authority === "function") {
const promise = authority(permissions);
// 函数执行后返回值是 Promise
if ((promise as Promise<boolean>) instanceof Promise) {
return (
<PromiseRender<T, K>
ok={target}
error={Exception}
promise={promise as Promise<boolean>}
/>
);
}
if (promise) {
return target;
}
return Exception;
}
throw new Error("Unsupported parameters");
};
- 页面使用案例
import React from "react";
import Authorized from "@/components/Authorized";
// 通用权限包装处理权限的设计一定要和后段确定好,哪些有权限,哪些没有权限,包括路由权限,页面权限,按钮级别权限。
export default function Demo() {
return (
<>
<AuthorizedButton authority="hello">Hello world</AuthorizedButton>
<AuthorizedButton authority={["hello", "word"]}>
Hello world
</AuthorizedButton>
<AuthorizedButton
authority={["hello", "word"]}
render={() => <div>Hello world</div>}
/>
<AuthorizedButton
authority={() => true}
render={() => <div>Hello world</div>}
/>
</>
);
}
与后端对接
-
一般我们都会有权限的配置页面,权限的配置页面可以配置项目的管理员,非管理员权限,以及对应角色所对应的权限,每个权限对应到代码里面,是1,2,3也好,还是什么也好你自己和后台处理好就好
-
包括按钮级别,说的天花乱坠,其实按钮也是组件,关键的是你们想怎么展示,有权限的展示,没权限是什么展示,这个没有定论,看你们产品业务心情,你根据业务去做就好了, 其次就是代码的封装,因为你们配置的权限数据,最后都会要返回到对应项目,还是前端处理的,后端只是一个中转,最后处理数据都是要你自己来的。切勿给自己挖坑。