1.这节我们先做操作,再来理清下原理,我们还是拿登录日志来做操作,我们的若依后台有两个登录账号admin和ry,默认admin是超级管理员,登录日志有个新建按钮,我们要实现用admin登录能看到新建按钮,而用ry登录,看不到新建按钮的效果。

    2.由于之前我们设置了/list/loginfo不能在地址栏中直接输入访问,我们现在要用登录日志做示例,就要放开权限,我们到config/config.js中注销掉登录日志的路由中的 // access: ‘authorize’,这行,注释后的登录日志完整路由配置为

    1. {
    2. name: 'loginfor',
    3. icon: 'smile',
    4. path: '/list/loginfo',
    5. component: './loginfo',
    6. // access: 'authorize',
    7. },

    3.在src/utils目录下新建permission.js文件,完整的permission.js代码如下

    1. export function matchPermission (permissions, value) {
    2. if(permissions === undefined)
    3. return false;
    4. const type = typeof value;
    5. if (type === 'string') {
    6. return matchPerm(permissions, value);
    7. }
    8. return matchPerms(permissions, value);
    9. }
    10. // /**
    11. // * 字符权限校验
    12. // * @param {Array} value 校验值
    13. // * @returns {Boolean}
    14. // */
    15. export function matchPerms (permissions, value) {
    16. if (value && value instanceof Array && value.length > 0) {
    17. const permissionDatas = value;
    18. const all_permission = '*:*:*';
    19. const hasPermission = permissions.some((permission) => {
    20. return all_permission === permission || permissionDatas.includes(permission);
    21. });
    22. if (!hasPermission) {
    23. return false;
    24. }
    25. return true;
    26. }
    27. console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`);
    28. return false;
    29. }
    30. export function matchPerm (permissions, value) {
    31. if (value && value.length > 0) {
    32. const permissionDatas = value;
    33. const all_permission = '*:*:*';
    34. const hasPermission = permissions.some((permission) => {
    35. return all_permission === permission || permissionDatas === permission;
    36. });
    37. if (!hasPermission) {
    38. return false;
    39. }
    40. return true;
    41. }
    42. console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`);
    43. return false;
    44. }
    45. /**
    46. * 角色权限校验
    47. * @param {Array} value 校验值
    48. * @returns {Boolean}
    49. */
    50. export function checkRole (roles, value) {
    51. if (roles && value && value.length > 0) {
    52. for(let i = 0; i< roles?.length; i ++) {
    53. for(let j = 0; j< value?.length; j ++) {
    54. if(value[j] === roles[i].roleKey) {
    55. return true;
    56. }
    57. }
    58. }
    59. }
    60. console.error(`need roles! Like checkRole="['admin','editor']"`);
    61. return false;
    62. }

    4.修改app.jsx中的fetchUserInfo代码,主要改下返回的结果,修改后的fetchUserInfo代码如下

    1. const fetchUserInfo = async () => {
    2. try {
    3. const msg = await queryCurrentUser();
    4. const currentUser={...msg.user,permissions: msg.permissions};
    5. console.log("login user info:",currentUser);
    6. return currentUser;
    7. } catch (error) {
    8. history.push(loginPath);
    9. }
    10. return undefined;
    11. };

    这里我们在fetchUserInfo中加入了, msg.permissions,也就是在全局初始数据的currentUser中会加入从后台拿到的当前用户的权限列表,超管的permissions是[‘::*’],ry的permissions是

    1. [
    2. 'system:user:resetPwd',
    3. 'system:post:list',
    4. 'monitor:operlog:export',
    5. 'monitor:druid:list',
    6. 'system:menu:query',
    7. 'system:dept:remove',
    8. 'system:menu:list',
    9. 'tool:gen:edit',
    10. 'system:dict:edit',
    11. ... //此处省略若干权限值
    12. ]

    这个permissions是个数组,代表当前登录用户有权操作的标识列表。超管的[‘::*’]代表啥都可以操作。

    5.打开src/access.js,先引入第3步中权限判断的方法

    1. import { matchPermission } from "./utils/permission";

    6.在access.js的access 函数的return结果中增加一些代码。完整的access函数代码如下

    1. export default function access(initialState) {
    2. const { currentUser, menuData } = initialState || {};
    3. return {
    4. hasPerms: (perm) => {
    5. return matchPermission(currentUser?.permissions, perm);
    6. },
    7. hasNoPerms: (perm) => {
    8. return !matchPermission(currentUser?.permissions, perm);
    9. },
    10. authorize: (route) => {
    11. if(menuData) {
    12. const items = getMatchMenuItem(route.path, menuData);
    13. if(!items || items.length === 0){
    14. return false;
    15. } else {
    16. return true;
    17. }
    18. }
    19. return true;
    20. }, // initialState 中包含了的路由才有权限访问
    21. };
    22. }

    其中authorize是上次我们做路由和菜单权限控制时用的,这里我们加了两个key hasPerms,hasNoPerms从字面意义就可以判断hasPerms是判断是不是有权限,返回true为有权限,
    hasNoPermss是判断没有权限,返回true为没有权限。

    7.打开登录日志操作的列表页面src/pages/loginfo/index.jsx,我们要判定的新建按钮在这个jsx文件里面,先import一段代码,在页面开头最后一行的impor后新起一行,加一个import

    1. import { useAccess } from 'umi';

    8.在const TableList = () => { 函数中的,第一行加一行代码 const access = useAccess();

    1. const TableList = () => {
    2. const access = useAccess();
    3. ......

    9.找到新建按钮的代码位置

    1. toolBarRender={() => [
    2. <Button
    3. type="primary"
    4. key="primary"
    5. onClick={() => {
    6. handleModalVisible(true);
    7. }}
    8. >
    9. <PlusOutlined /> 新建
    10. </Button>,
    11. ]}

    在Button上增加一行代码 hidden={access.hasNoPerms(‘system:loginfo:add’)}

    1. toolBarRender={() => [
    2. <Button
    3. type="primary"
    4. key="primary"
    5. hidden={access.hasNoPerms('system:loginfo:add')}
    6. onClick={() => {
    7. handleModalVisible(true);
    8. }}
    9. >
    10. <PlusOutlined /> 新建
    11. </Button>,
    12. ]}

    这里的system:loginfo:add是我们自定义的一个权限标识,标识对登录日志的新增权限,这个权限超管的permision中没有但是配置了[‘::*’],超管他是有权限的,而ry的permision列表里面肯定没有这个system:loginfo:add权限标识,所以这个按钮超管可见,而ry不可见。

    10.保存前端代码,编译后先用admin登录,然后在地址栏中输入list/loginfo看是否能见到新建按钮,截图如下
    image.png

    11.切换ry用户登录,然后再地址栏中如如list/loginfo,看能否见到新建按钮,截图如下
    image.png
    通过当前的登录用户的权限列表值,permissions可以控制页面按钮级别的显示、隐藏,这个permission的值,我们在后台可以配置。这样就可以实现页面按钮级别的权限控制了。

    12.做完操作,看到效果,我们再总结下原理,我们可以从后台获取到当前用户的permissions,将其写入全局初始数据currentUser中,然后在access.js中可以配置是否有权的key hasPerms或者hasNoPerms,传入一个当前的操作权限值如system:loginfo:add,根据currentUser中当前用户的权限列表permissions,我们可以判断当前用户是否有system:loginfo:add的操作权限,根据权限值真假,来控制按钮显示或者隐藏,permissions后台可配,另外如果要实现根据不同角色控制按钮的权限,原理类似。