# 权限控制

  • 在 Web 系统中,权限很久以来一直都只是后端程序所控制的。和数据库最紧密接触的是后端程序。所以在很长的一段时间内,权限一直都只是后端程序要考虑的话题。但是随看前后端分离架构的流行,越来越多的项目也在前端进权限控制。

# 权限的相关概念

# 权限的分类

  • 后端权限
    • 从根本上讲前端仅仅只是视图层的展示,权限的核心是在于服务器中的数据,所以后端才是权限的关键,后端权限可以控制某个用户是否能够查询数据, 是否能够修改数据等操作。
    1. 后端如何知道该请求是哪个用户发过来的?
      • 状态管理 cookie、session、token
    2. 后端的权限设计 RBAC (Role-based Access Control)
      • 用户、角色、权限
  • 前端权限
    • 前端权限的控制本质上来说,就是控制端的视图层的展示和前端所发送的请求。但是只有前端权限控制没有后端权限控制是万万不可的。前端权限控制只可以说是达到锦上添花的效果。

# 前端权限的意义

  • 如果仅从能够修改服务器中数据库中的数据层面上讲, 确实只在后端做控制就足够了,那为什么越来越多的项目也进行了前端权限的控制,主要有这几方面的好处:
    1. 降低非法操作的可能性
    2. 尽可能排除不必要清求, 减轻服务器压力
    3. 提高用户体验
      • 根据用户具备的权限为该用户展现自己权限范围内的内容, 避免在界面上给用户带来困扰, 让用户专注于分内之事。

# 前端权限控制思路

# 菜单的控制

  • 在登录请求中,会得到权限数据,当然,这个需要后端返回数据的支持.前端根据权限数据,展示对应的菜单,点击菜单,才能查看相关的界面

# 界面的控制

  • 如果用户没有登录, 动在地址栏敲入管理界面的地址,则需要跳转到登录界面。
  • 如果用户已经登录,如果手动敲入非权限内的地址,则需要跳转 404 界面。

# 按钮的控制

  • 在某个菜单的界面中,还得根据权限数据,展示出可进行操作的按钮,比如删除, 修改, 增加。

# 请求和响应的控制

  • 如果用户通过非常规操作,比如通过浏览器调试工具将某些禁用的按钮变成启用状态,此时发的请求, 也应该被前端所拦截。

# 前端权限实现的思路

# 权限菜单栏控制

  • 用户登录之后服务端返回一个数据,这个数据有菜单列表和 token,我们把这个数据放入到 vuex 中,然后主页根据 vuex 中的数据进行菜单列表的渲染。
    • 问题: 刷新界面 vuex 数据消失,菜单栏消失,怎么办?
    • 解决: 将数据存储在 sessionStorage 中,并让其和 vuex 中的数据保持同步。
    • 可使用插件 vuex-persistedstate (opens new window)

# 界面的控制

  • 登录成功后,将 token 数据存储在 sessionStorage 中,判断是否登录。
  1. 路由导航守卫

    router.beforEach((to, from, next) => {
    if (to.path === '/login') {
        next();
    } else {
        const token = sessionStorage.getItem('token');
        if (!token) {
            next('/login');
        } else {
            next();
        }
    }
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    • 问题: 这样用户在登录之后就可以访问其他界面了,但如果用户 A 登录之后他只能访问 a 页面,他不能访问 b 页面,但是这时候他还是可以通过地址栏输入进入到 b 页面。
    • 解决: 当然我们也可以设置路由导航守卫,但是如果有多个页面,设置会非常不方便,并且对于用户 A 来说,它是不用访问 b 页面的,这时候我们何不对 A 不显示 b 页面,这个时候我们就用到了动态路由。
  2. 动态路由

  • 根据当前用户所拥有的的权限数据来动态添加所需要的路由,将这个封装成一个方法。

  • 登录成功之后动态添加路由,注意这个封装的方法需要暴露出去在登录页面调用。

    • 问题: 如果我们重新刷新的话动态路由就会消失,动态路由是在登录成功之后才会调用的,刷新的时候并没有调用,所以动态路由没有添加上。
    • 解决: 可以在 app.vue 中的 created 中调用添加动态路由的方法。
  1. 按钮的控制
  • 虽然用户可以看到某些界面了,但是这个界面的一些按钮该用户可能是没有权限的。 因此,我们需要对组件中的一些按钮进行控制,将用户不具备权限的按钮就隐藏或者禁用,而在这块的实现中,可以把该逻辑放到自定义指令中。

    <button v-permission="{action: 'add', effect: 'disabled'}">添加用户</button>
    
    1
    import Vue from 'vue';
    import router from '@/router.js';
    
    Vue.directive('permission', {
    // 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
    inserted(el, binding) {
        // 对应自定义指令中的 add
        const action = binding.value.action;
        // 你可以将权限数据存到当前路由的元信息中
        const currentRight = router.currentRoute.meta;
        if (currentRight) {
        if (currentRight.indexOf(action) === -1) {
            // 不具备权限
            const type = binding.value.effect;
            if (type === 'disabled') {
                // 禁用该节点
                el.disabled = true;
            } else {
                // 移除该节点
                el.parentNode.removeChild(el);
            }
        }
        }
    },
    });
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  1. 请求和相应的控制
  • 请求控制
    • 除了登录请求都得要带上 token ,这样服务器才可以鉴别你的身份,这块使用的就是 axios 的请求拦截器设置。
    • 如果发出了非权限内的请求,应该直接在前端范围内阻止,虽然这个请求发到服务器也会被拒绝。
    • 非权限内的请求:比如 a 用户是不能够操作该页面的按钮的,但是他通过 f12 调试把按钮改为可点击,如果我们不对这个请求进行处理,那么这个请求就会发送出去。
  • 响应控制
    • 得到了服务器返回的状态码 401, 代表 token 超时或者被篡改了,此时应该强制跳转到登录界面。

# 小结

  • 前端权限的实现之须要后端提供数据支持, 否则无法实现。
  • 返回的权限数据的结构,前后端需要沟通协商怎样的数据便用起来才最方便

# 菜单控制

  • 权限的数据需要在多组件之间共享, 因此采用 vuex
  • 防止刷新界面,权限数据丢失,所以需要存在 sessionStorage,并目要保证两者的同步。

# 界面控制

  • 路由的导航守卫可以防止跳过登录界面。
  • 动态路由可以让不具备权限的界面的路由规则压根就不存在。

# 按钮控制

  • 路由规则中可以增加路由元数据 meta
  • 通过路由对象可以得到当前的路由规则以及存在此规则中的 meta 数据
  • 自定义指令可以很方便的实现按钮控制

# 请求和响应控制

  • 请求拦截器和响应拦截器的使用
  • 请求方式的约定restful

版权声明:本文为CSDN博主「胡歌的小奶音」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_44157964/article/details/108420759