1.菜单能正常加载的话,加载的菜单都是用户有权访问的,没权访问的不会显示出来,但是有个明显的权限问题,就是用户如果知道某个路由的访问url地址的话,直接在地址栏中输入url地址,是会正常加载该url的。比如我们之前做的登录日志列表的页面访问地址是/list/loginfo,我们先成功登录后,然后在地址栏中输入/list/loginfo,,登录日志列表页面是会正常加载的,如下图所示
    image.png

    2.如果我们想控制在地址栏中直接输入/list/loginfo,提示403,该如何实现呢?我们先参照官网文档路由和菜单的权限控制,理下思路。

    1. 如果需要对路由还有菜单进行权限控制,可以直接在路由上原有基础配置上加上权限控制相关的属性,即可快速实现路由和菜单的权限控制。(前提需要使用最佳实践的 Layout 方案 - @alipay/umi-plugin-layout )。
    2. 在以上定义(src/access.ts, src/app.ts)完成的基础上,再在路由配置项上添加 access 属性即可完成路由和菜单的权限控制。access 属性的值为 src/access.ts 中返回的对象的 key。以下为实际例子:
    3. 假设权限定义文件 src/access.ts 内容如下:
    4. // src/access.ts
    5. export default function (initialState = {}) {
    6. const { isAdmin, hasRoutes = [] } = initialState;
    7. return {
    8. // ...
    9. adminRouteFilter: () => isAdmin, // 只有管理员可访问
    10. normalRouteFilter: (route) => hasRoutes.includes(route.name), // initialState 中包含了的路由才有权限访问
    11. };
    12. }
    13. 通过以上示例可以看到,权限路由控制相关的函数,接收"当前处理的路由"作为第一个参数
    14. 那么只需要按以下方式在常规路由配置中加上 access 这一项即可:
    15. // config/config.ts
    16. import { defineConfig } from 'umi';
    17. export default defineConfig({
    18. routes: [
    19. {
    20. path: '/foo',
    21. name: 'foo',
    22. // ...
    23. access: 'normalRouteFilter', // 会调用 src/access.ts 中返回的 normalRouteFilter 进行鉴权
    24. },
    25. {
    26. path: '/admin',
    27. name: 'admin',
    28. // ...
    29. access: 'adminRouteFilter', // 会调用 src/access.ts 中返回的 adminRouteFilter 进行鉴权
    30. },
    31. ],
    32. // ...
    33. });
    34. 对应鉴权函数(比如 adminRouteFilter)在接收路由作为参数后返回值为 false,该条路由将会被禁用,并且从左侧 layout 菜单中移除,如果直接从 URL 访问对应路由,将看到一个 403 页面。

    3.官网这段话比较难理解,本人经过实践操作,确定如果要实现直接从 URL 访问对应(无权)路由,将看到一个 403 页面的效果,操作制步骤如下,先在src/access.ts中定义权限标识,比如在access.js(js版是js)中我们定义一个key叫 authorize的函数,authorize: (route) =>,官网上这句话:权限路由控制相关的函数,接收”当前处理的路由”作为第一个参数,也就是authorize的参数route就是当前正在访问的路由,然后我们可以得到当前用户有权访问的所有菜单,遍历菜单跟route参数做对比,如果route不在有权访问的菜单数组中,我们可以返回false,返回false即无权访问。下一步还要改config/config.js下的路由配置,我们的登录日志列表是配置在这里的,原始路由配置如下:

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

    我们在要控制的路由其中加一行access:authorize即可实现效果,authorize是我们之前在access中配置的key authorize,即

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

    4.这里有一个小问题就是如何得到当前用户有权访问的所有菜单,我们之前是可以通过函数获取的,当然也可以再调用一下函数来获取,如果频繁要调用的数据,我们可以考虑放入initialState,全局初始数据中

    5.原理我们理清后,我们来做操作,完整的操作步骤如下,修改app.tsx,拿到菜单数据后,将放入到全局初始数据中

    1. export const layout = ({ initialState,setInitialState }) => {

    layout中要传入setInitialState方法

    1. menu: {
    2. // 每当 initialState?.currentUser?.userId 发生修改时重新执行 request
    3. params: {
    4. userId: initialState?.currentUser?.userId,
    5. },
    6. request: async (params, defaultMenuData) => {
    7. const tempMenuData = await getCurrentUserMenus();
    8. const menuData=fixMenuItemIcon(tempMenuData);
    9. setInitialState({
    10. ...initialState,
    11. menuData: menuData,
    12. });
    13. return menuData;
    14. },

    setInitialState中放入拿到的菜单数据menuData

    6.打开src/services/ant-design-pro/menu.js,增加判定当前访问路由是否在有权限访问的菜单数组中的方法 getMatchMenuItem,

    1. export function getMatchMenuItem(path, menuData){
    2. if(!menuData)
    3. return [];
    4. let items= [];
    5. menuData.forEach((item) => {
    6. if (item.path) {
    7. if (item.path === path) {
    8. items.push(item);
    9. }
    10. if (path.length >= item.path?.length) {
    11. const exp = `${item.path}/*`;
    12. if (path.match(exp)) {
    13. if(item.children) {
    14. const subpath = path.substr(item.path.length+1);
    15. const subItem = getMatchMenuItem(subpath, item.children);
    16. items = items.concat(subItem);
    17. } else {
    18. const paths = path.split('/');
    19. if(paths.length >= 2 && paths[0] === item.path && paths[1] === 'index') {
    20. items.push(item);
    21. }
    22. }
    23. }
    24. }
    25. }
    26. });
    27. return items;
    28. }

    7.打开src/access.js,先引入getMatchMenuItem方法,按照之前说的逻辑,增加authorize: (route) => {…}函数,完整的access.js代码如下:

    1. /**
    2. * @see https://umijs.org/zh-CN/plugins/plugin-access
    3. * */
    4. import { getMatchMenuItem } from "./services/ant-design-pro/menu";
    5. export default function access(initialState) {
    6. const { currentUser, menuData } = initialState || {};
    7. return {
    8. authorize: (route) => {
    9. if(menuData) {
    10. const items = getMatchMenuItem(route.path, menuData);
    11. if(!items || items.length === 0){
    12. return false;
    13. } else {
    14. return true;
    15. }
    16. }
    17. return true;
    18. }, // initialState 中包含了的路由才有权限访问
    19. };
    20. }

    access.js中的menuData是从initialState中拿的。

    8.修改config/config.js中登录日志的路由配置,代码如下:

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

    9.编译后,重启前台程序,这时候我们再登录后,在浏览器中直接访问/list/loginfo,就会返现报403了,截图如下:
    image.png