work-权限总结

110 阅读6分钟

工作需求评了一个权限,下面记录一下做完的总结

背景

  1. 菜单构成为三级菜单:一级菜单作为主类,选择后出现二级三级菜单,二三级菜单嵌套展示在侧边栏。
  2. 角色权限设置也同菜单构成一样是三级
  3. 账号登录后根据不同权限开放权限内页面

实现结果

image.png image.png

image.png

image.png

严重的问题

问题一

这里的权限跟服务端同事沟通后是以前端为主,意思是我想咋弄咋弄服务端只给我一个表存数据。服务端仅仅需要code(标识),我当时定义的是路由path,这里最傻的地方是我强迫症非要给他路径名称,而不是路径。

route.path = '/path'这里路径名称我定义为path,路径定义为/path,就因为这个看似不起眼的地方前端部分要做很多的数据处理且没啥太大意义。因为不论是路径名称还是路径对于服务端或者说是类型上它就是一个字符串。

问题二

菜单高亮失效。

在配置完权限后,正常运作中没啥问题,但是在地址栏直接输入对应的菜单栏不高亮。

开始

1

首先先画一个流程图再去写代码

image.png

这里唯一要注意的点就是前端作为权限源,路由规则一旦生成就不能变(不影响之后而是保证之前),这里就是比较尬的地方了,后端主导去做权限的话(会定义好规则存数据库,这样前端变动后端不受影响),但是前端做主导后端会很很爽至少我这么认为,至于安全性方面(前端代码百分百能爬到就是成本问题需要解析才能展示源码,至于身份令牌接口规则都涉及成本问题,所以不用担心)。

2

配置路由规则

考虑点:

  1. code属性要满足唯一性,选取path
  2. 导航菜单属性依赖key,属性要满足唯一性以及自定义性,选取meta,定义一个name,一个Cname
  3. 导航菜单名展示,属性满足自定义性,定义meta.title
  4. 导航菜单嵌套和不嵌套,判断属性基于导航菜单名,选取meta.fTitle(另一个方案直接改路由属性就可以使用path)
  5. 至于三级菜单栏的设计方案我选择直接塞meta定义Tname(也可以直接使用路由嵌套嵌套)
  6. 图标就使用iconfont线上图标直接带参数就行定义为meta.icon

开始编写(代码逻辑根据不同情况不同,下面只叙述思路)

  1. 配置好路由
  2. 去到主页面容器配好菜单栏
  3. 再去store里面,注册权限路由
  4. 编写store控制的路由name Cname Tname的存取方法
  5. 在导航守卫里面存路由name Cname Tname
  6. 在主页面容器取name Cname Tname
  7. 完成以上就能实现选取菜单跳转对应页面且地址输入跳转对应页面且菜单高亮样式保持一致,这里主动添加element的类is-active不然地址切换路由会有样式问题(问题二)
  8. 接下来写权限
  9. 定义好的接口是服务端返回privs:string[]这个就是权限数组,这里有个小插曲(这个字段是在登录的时候跟token一起返回的,正常来说应该登录获取token然后再根据token去拿用户信息,因为存在刷新清空vuex的问题,虽然能够解决刷新vuex但是这样数据就是死的了,正常的逻辑是刷新清空了vuex的时候会触发一个事件就是vuex没值了,但是存在token就再次调用接口token获取用户信息且这个逻辑也可以放在用户信息被修改,同样后端会有权限验证的,所以根据定义这个情况的code码可以在响应拦截里面触发)
  10. 定义一个超级管理员的角色(因为前端控制权限所以只要跟服务端定义好超管角色标识就行),这个超管直接注册所有路由,不需要去管privs
  11. 通过超管去设置角色权限,在角色设置页面将静态路由展示出来,进行勾选,提交给服务端
  12. 非超管登陆时,将privs里面的元素跟静态路由做筛选,满足条件的注册到路由里,这样就完成了权限
  13. 这里权限仅到页面级(按钮级别同理,只不过是在meta中继续扩展而已)
  14. 至于服务端怎么验证,就是前后约束好,页面path塞到请求头里面
  15. 校验处理,当整个权限逻辑写完后,由于服务端自定义的超管权限是null因为只做角色检查第10步,所以会有类型错误要做好处理,或者让服务端给一个数组类型,或者自己添加保存

示例代码:

// role.ts
export const role =	{
    path: '/role',
    name: 'Role',
    component: () => import('@/views/index/index.vue'),
    meta: {
            title: '角色管理',
            Cname: 'rolePrivs',
            name: 'Role',
            fTitle: '角色管理',
            icon: '\ue6e1',
            Tname: '权限设置',
    },
    children: [
        {
            path: '/rolePrivs',
            component: () => import('@/views/authority/role/Index.vue'),
            name: 'rolePrivs',
            meta: {
                title: '角色权限设置',
                name: 'rolePrivs',
                fTitle: '角色管理',
                Tname: '权限设置',
            }
        },
    ]
}
// baseRoute.ts
import { role } from './asyncRoute/authority/role';
let baseRoutes = [] as any[];
baseRoutes.push(role);
export default baseRoutes;
// index.ts
// 获得用户信息
async getInfo({commit}:any){
    const token = getCookie('token');
    const data = JSON.parse(getCookie('userInfo'))
    commit("SET_TOKEN",token);
    commit("SET_USERNAME",data.username);
    commit("SET_PRIVS",data.permissionList);
    commit("SET_ADMIN",data.admin);
    if(data.admin == 1){
        baseRoutes.forEach((item:any) => {
            router.addRoute(item)
        })
    }else{
        baseRoutes.forEach((item: any) => {
            if(data.permissionList.includes(item.path.substring(1))){
                router.addRoute(item);
            }else{
                item.children = item.children.filter((itemm: any) => {
                    if(data.permissionList.includes(itemm.path.substring(1))){
                        return itemm;
                    }
                })
                if(item.children.length != 0){
                    item.meta.Cname = item.children[0].path.substring(1);
                    router.addRoute(item);
                }
            }
        })
    }
    router.getRoutes()[0].redirect = router.getRoutes()[3].path;
}

可以优化的点:

Cname在注册路由时写在方法里面

扩展一下权限控制层级至按钮

  1. 如何展示在角色权限配置中
  2. 路由中怎么去设置按钮权限
  3. 对应的权限怎么在前端控制样式
  4. 按钮权限怎么传递给服务端(场景:前端隐藏了按钮,这个按钮绑定接口a,但是接口a被工具直接访问携带的是这个token以及页面权限信息)

理论方案

  1. 首先按钮权限配置一定是在meta中,根据前端展示的按钮进行分配。
  2. 理想展示样式一定是树状图勾选按钮权限作为三级嵌套进去,(直接对数组进行操作,深拷贝路由数组对拷贝数组进行处理),不想这样就简单的在谈一个区域去进行按钮权限展示。
  3. 控制样式直接在dom层面v-if
  4. 至于传递给服务端就约定好公共接口不做验证,其余都做验证,前端在传输中可以放在参数中也可以拦截放在请求头中。