四、项目权限梳理


1、登录流程

  • 进入登录页,获取验证码
// 文件位置
// src\pages\Login\Login.vue

mounted() {
  this.setIdentifierObj();
}

private async setIdentifierObj() {
  const res = await getIdentifierImg();
  if (res) {
    const { img, cToken } = res;
    if (img && cToken) {
      this.identifierObj.imgUrl = img;
      this.identifierObj.cToken = cToken; // 比对验证码是否正确
    }
  }
}
  • 点击登录,首先因为平台使用第三方库crypto-js对登录密码进行加密。所以首先我们会调用接口,请求密钥。
// 文件位置
// src\pages\Login\Login.vue

const skRes = await userApi.getSk();
if(skRes) {
  const { data } = skRes;
  const dataArr = data.split('');
  dataArr.splice(0, 1, 'E');
  dataArr.splice(7, 1, 'D');
  dataArr.splice(15, 1, 'U');
  const md5Str: string = dataArr.join('');
}
  • 拿到密钥后对密码进行加密
// 文件位置
// src\pages\Login\Login.vue

const aesKey = {
  key: md5Str,
  iv: '',
  modeType: ''
}
JsCrypto.setDataAndAesKey(loginFormModal.password, aesKey);
  • 调用登录接口,验证登录
// 文件位置
// src\pages\Login\Login.vue

const res = await userApi.login({ 
  ...loginFormModal, 
  cToken: this.identifierObj.cToken, 
  textCode: this.identifier 
});
  • 登录成功,拿到返回的token,并存储token,localstorage和cookie中各存一份。目前看存储到cookie没啥太大意义还徒增请求头体积。
// 文件位置
// src\pages\Login\Login.vue

this.$cookies.set('access_token', accessToken);
localStorage.userToken = accessToken;
  • 关于记住密码,存储到cookie有问题,请求的时候会携带cookie徒增请求头体积,第二不持久,因该存储到localstorage里
// 文件位置
// src\pages\Login\Login.vue

if (rememberPassword) {
  const { aesPassword, aesKey } = userInfo;
  this.setUserInfoToCookie({
    password: aesPassword,
    account: this.loginFormObj.account,
    aesKey: aesKey
  });
} else {
  this.$cookies.remove('accountInfo');
}
  • 后续请求,携带token
// 文件位置
// src\http\interceptors.ts

this.instance.interceptors.request.use((config) => {
  // .......
  const token = localStorage.userToken
  if (token) {
    config.headers.Authorization = token
  }
  // .......
});

2、路由权限与路由跳转

  • 登录成功后,会跳转到路由:/home/atlasManage/home/structure,此为三级路由
// 文件位置
// src\pages\Login\Login.vue

function loginSuccess(res, rememberPassword?, userInfo?) {
  this.$router.push({ 
    path: `/home/atlasManage/home/structure?${strArr.join('&')}` 
  })
}
  • 访问路由,进入路由前置首守卫,在路由守卫里做判断,比如如果没有token,跳转到登录页,随后调用接口i请求当前用户下所有的路由菜单**(根据token里的用户基本信息判断该返回哪些路由)**。
// 文件位置
// src\router\index.ts
const res = await userApi.getMunuList({
  system: 'jk'
});
  • 具体的路由返回是根据角色配置的,在角色管理页面给角色分配路由,再给用户分配角色,这样用户就有了对应的路由。以内容合规审核员为例,给内容合规审核员分配路由权限

  • 获取到所有的路由数据后,会对路由数据做处理转成符合VueRouter路由的配置项的格式。同时我们回想路由的第一层push一个路由配置
// 文件位置
// src\router\index.ts
const routerObj: Array<any> = [
  {
    path: '/home',
    component: () => import('@/pages/Layout/Layout.vue'),
    children: []
  }
];
// 递归函数处理路由数据
recursionFun(menuList, routerArr);
routerObj[0].children = routerArr;
  • 此时App.vue组件的结构如下,TopNav是顶部导航栏,而对应的router-view渲染我们的一级路由/home,也就是Layout.vue组件,具体结构图如下。
<template>
  <div id="app">
    <TopNav></TopNav>
    <router-view />
  </div>
</template>

  • 在TopNav.vue组件内部如下,左侧为侧边栏,右侧为router-view将渲染的二级路由内容
<template>
  <Layout class="main-container" :class="{ 'show-side-nav': showSideNav }">
    <SideNav
      :parentId="$store.state.common.currentMenuId"
      v-show="showSideNav"
      :class="{ 'is-expand': $store.state.common.sideExpand }"
      ></SideNav>
    <router-view class="page-content" :class="{'not-padding':notPadding}" />
  </Layout>
</template>

  • 我们会将二级路由的路径全部绑定到左侧的侧边栏,所以当我们点击侧边栏的时候,会push到二级的路由,也就是渲染右侧的二级路由内容。点击左侧侧边栏即可切换路由,切换右侧的内容。
public menuClick (name: string): void {
  const urlLink: string = name + this.getQueryParam(name);
  this.$router.push({
    path: urlLink
  });
}
  • 切换顶部导航栏,首先是改变了currentMenuId,随后跳转路由到/home/authManage

public menuClick (menuData: any): void {
  this.$store.commit('changeCurrentId', this.activeId);
  this.$router.push({
    path: urlLink
  })
}
  • 首先左侧侧边栏监听了currentMenuId,会根据它做筛选,不同的Id展示不同的侧边栏,侧边栏对应不同的路由路径。其次右侧的内容会变为/home/authManage对应的组件:/Auth/index.vue
<template>
  <div class="auth-manage-container">
    <div class="right-container">
      <div class="header-container common-header-container">
        <span class="title">{{ headerTitle }}</span>
      </div>
      <router-view></router-view>
    </div>
  </div>
</template>
  • 同时在前置路由守卫里给/home/authManage路由增加了一个路由配置,它对应的就是/home/authManage/user路由对应的/Auth/UserVue.vue
if (item.link === '/home/authManage') {
  obj.children.unshift({
    path: '/',
    component: obj.children[0].component
  });
}
  • 关于标注平台,是通过微服务qiankun框架注册的子应用。在main.ts中有
// 文件位置
// src\main.ts
// 引入子应用配置
getQianKunConfig().then((result) => {
  (window as any).qiankun.registerMicroApps(result.subApps, {
  })
}).catch((err) => {
  console.log(err);
});
  • getQianKunConfig返回的格式为
{
  "subApps": [
    {
      "name":"qianKunMarkManage",
      "entry":"https://fxm-graph-jk.iflyresearch.com/qianKunMarkManage/",
      "activeRule":"#/qianKunMarkManage",
      "container": "#sunIframe",
      "props": {
        "baseUrl":"https://fxm-graph-jk.iflyresearch.com/qianKunMarkManage/"
      }
    }
  ]
}
  • 最终渲染在App.vue
<template>
  <div id="app">
    <div id="sunIframe" :class="{ 'is-qiankun': isQiankun }"></div>
  </div>
</template>

3、功能权限(按钮权限)

  • 权限管理 -> 功能列表增加功能权限,以增加一个删除资源为例,英文名称为:btn_deleteResource

  • 新增完之后在菜单管理给路由增加这个新增删除的功能,这里需要注意:

如果功能权限里什么都不配置,那么说明这个路由下不设置按钮权限;

如果配置了,则说明这个路由下的这些按钮就会被赋予权限,不经过进一步的处理是看不见的;

  • 这里给试卷管理配置了按钮权限后,需要回到角色管理,给角色增加按钮权限。比如给内容合规审核员的试卷管理路由配置展示编辑标签按钮,但是不展示资源变更按钮。

  • 这个时候我们进入到内容合规审核员的账号

  • 具体的实现是搭配自定义指令v-has实现,在试卷管理的列表页,我们使用v-has绑定了需要增加权限的按钮
<!-- 文件位置 -->
<!-- src\pages\AtlasManage\ResourceManage\list.vue -->
<Button v-if="canShowChangeResource(item)" 
  size="small" 
  :disabled="getResult(item, btnItem.code, !btnItem.canHandle)" 
  @click="handleBtnClick(btnItem.code, item, index)" 
  v-has:code="'btn_' + btnItem.code"
  >{{ setBtnName(item, btnItem.name) }}
</Button>
  • 然后在自定义指令里代码如下,判断如果绑定了v-has并且没有出现在路由配置里,则不渲染。按钮是和路由对应的看下数据结构即可。
// 文件位置
// src\customDirective.ts
if (unionPromissions.length > 0 && currentBtnValue !== 'btn_undefined') {
  if (unionPromissions && unionPromissions.indexOf(currentBtnValue) < 0) {
    // 开发过程中去掉这里todo
    (el as any).parentNode.removeChild(el);
  }
}


文章作者: 吴俊杰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吴俊杰 !
 上一篇
一、浏览器事件循环 一、浏览器事件循环
你是否真的了解js单线程,你是否真的了解事件循环,你是否真的了解浏览器的进程模型,你是否真的了解任务队列优先级......
2024-12-04
下一篇 
二、Nginx的https配置解决方案 二、Nginx的https配置解决方案
不会后端的前端不是一个好前端,凡是慢慢来,慢慢了解介于前后端中间的地界,再慢慢转向学习后端,你的最低目标是全栈啊......
2024-12-03
  目录