环境

前端框架:fantastic-admin框架

前言

前端项目现在是多入口的,分为portal和console两个入口,当前的需求是portal进去登录如果无授权的话直接退出登录,console入口进去无授权的话页面跳转到授权管理页面,且其他菜单全部隐藏掉。

实现思路

1.首先我们需要封装一个公共的方法去调用授权验证接口查看是否已经授权,并返回是否授权与授权失败的菜单code码去生成相应的菜单。并且在config配置文件重添加授权校验的开关。

// config_portal.js & config_console.js
var config = {
    app: 'portal',
    title: 'MINERVA',
    appName: '自服务',
    // ws: 'ws://172.20.21.46:39219',
    // 授权开关
    enableLicence: true,
    service_login: '/service_login/sys',
    service_minerva: '/service_minerva/v3',
    service_jumpserver: '/service_jumpserver',
    service_licence: '/service_minerva',
    // 前端
    client_iam: 'http://172.20.21.46:36866/iam_console',
    // jupyter
    client_jupyter: '/codeditor',
    currency_name: '算力币',

    // jumpserver
    jumpserver: {
        // status: true,
        token: '87d4937428b7f56ea0e36ba2452fc2280a329218',
        account: 'null',
        host: 'http://172.16.150.228',
        username: 'admin',
    },
    secretKey: 'TrendyBoot202312',
};

document.title = config.title;
// authStore.js
import { getValidateInfo } from '@/api/auth';
import router from '@/router';

const useAuthStore = defineStore(
    // 表示ID
    'auth',
    {
        state: () => ({
            isAuth: false,
            messageOptions: {
                portal: '权限已过期,请联系所属管理员',
                console: '权限已过期,请重新登录系统',
            },
			// 无授权时通过这个code码只生成授权管理的菜单(code码根据自己的业务逻辑来)
            noAuthCodeList: ['MINERVA_SYSTEM_MANAGEMENT_ADMIN', 'MINERVA_SYSTEM_MANAGEMENT_CUSTOMER'],
        }),
        actions: {
            // 判断当前是否有授权
            getAuthStatus() {
                if (!config.enableLicense) {
                    return Promise.resolve({
                        isAuth: true,
                        codeList: this.noAuthCodeList,
                    });
                }

                return new Promise((resolve) => {
                    getValidateInfo()
                        .then((res) => {
                            if (Object.values(res).includes(false)) {
                                this.handleNoAuth();
                            } else {
                                this.isAuth = true;
                            }
                        })
                        .catch(() => {
                                this.handleNoAuth();
                            })
                        .finally(() => {
                            resolve({
                                isAuth: this.isAuth,
                                codeList: this.noAuthCodeList,
                            });
                        });
                });
            },
            handleNoAuth() {
                this.isAuth = false;
                ElMessage({
                    message: this.messageOptions[config.app],
                    type: 'error',
                });
                // 自定义没有权限逻辑处理
                if (config.app === 'portal') {
                    this.clearOut().then(() => {
                        router.push('/login');
                    });
                }
            },
        },
    },
);

export default useAuthStore;

2. 根据方法返回信息做不同的菜单生成逻辑

1.1 授权成功,生成页面菜单,portal入口跳转第一个菜单页面,console入口跳转资源视图菜单页面

1.2 授权失败,portal入口清空用户信息跳转登录页,console入口只生成授权管理页面的动态菜单直接跳转授权管理页面

// 此处截取了routeStore中根据前端菜单code码配置和后端返回的权限code比较生成前端菜单的部分代码
generateRoutesAtFront(asyncRoutes) {
  // eslint-disable-next-line no-async-promise-executor
  return new Promise(async resolve => {
    // debugger
    const settingsStore = useSettingsStore()
    const userStore = useUserStore()
    const tabbarStore = useTabbarStore()
    const authStore = useAuthStore()
    let permissions = []
    // 先进行授权校验
    const { isAuth, codeList } = await authStore.getAuthStatus()
    let accessedRoutes
    // 如果权限功能开启,则需对路由数据进行筛选过滤
    if (settingsStore.app.enablePermission) {
      if (isAuth) {
        permissions = await userStore.getPermissions()
      } else {
        permissions = codeList
      }
      accessedRoutes = filterAsyncRoutes(
        asyncRoutes,
        permissions
      )
    } else {
      accessedRoutes = deepClone(asyncRoutes)
    }
    console.log('菜单', deepClone(accessedRoutes))
    // 设置 routes 数据
    this.isGenerate = true
    this.routes = accessedRoutes.filter(
      item => item.children.length != 0
    )
    // 修改菜单顶层路由的redirect 默认指向第一个
    this.routes.map(v => {
      if (v.children?.length) {
        v.redirect = v.children[0]?.redirect || v.redirect
      }
      return v
    })
    // 加载常驻标签页
    if (settingsStore.tabbar.enable) {
      tabbarStore.initPermanentTab()
    }
    resolve()
  })
}

// menuSotre中截取的设置进入主页后进入的第一个页面的方法
sidebarMenusFirstDeepestPath() {
  const authStore = useAuthStore()
 // 跳转资源视图页面
  if (authStore.isAuth && config.app == 'console') {
    return '/resource_management/resource_view_management/list'
  }

  return this.allMenus.length > 0
    ? getDeepestPath(this.sidebarMenus[0])
    : '/'
}

3. 处理一些特别情况,如登录系统后过一段时间授权过期了,这时候接口会返回报错,根据接口报错做一系列处理

// api/index.js
const authCodeList = [900000,900001,900002,900003]
// 截取axios相应拦截器中部分错误处理代码
else if (authCodeList.includes(error.response.data.code)) {
  //authStore中的是否授权判断也会提示信息,与这边重复了,后续需要处理
  if (window.config.app === 'portal') {
    ElMessage({
    message:'授权已过期,请联系系统管理员',
    type: 'error'
    })
    toLogin()
  } else if (window.config.app === 'console') {
    ElMessage({
    message: error.response.data.message,
    type: 'error'
    })
  }
}