后台管理系统的权限控制与管理

一、权限相关概念

1.1 权限的分类
(1)后端权限
从根本上讲前端仅仅只是视图层的展示,权限的核心是在服务器中的数据变化,所以后端才是权限的概念,后端权限可以控制某个用户是否能够查询数据,是否能够修改数据等操作

  1. 后端如何知道该请求是哪个用户发过来的
    cookie
    session
    token
  2. 后端的权限设计RBAC
    用户
    角色
    权限
    (2)前端权限
    前端权限的控制本质上说,就是控制端的视图层的展示和前端所发送的请求。但是只有前端权限控制没有后端权限控制是万万不可得,前端权限控制只可以说是达到锦上添花的效果
    1.2 前端权限的意义
    如果仅从能够修改服务器中数据库中的数据层面上讲,确实只在后端做控制就足够了,那为什么越来越多的项目也进行了前端权限的控制,主要这几个方面的好处
  • 降低非法操作的可能性
    不怕贼头就怕贼惦记,在页面中展示出一个就算点击了也会最终失败的按钮,势必会增加有心者的非法操作的可能性
  • 尽可能排除不必要的请求,减轻服务器压力
    没必要的请求,操作失败的请求,不具备权限的请求,压根就不需要发送,请求少了,自然也会减轻服务器的压力
  • 提高用户体验
    根据用户具备的权限为该用户展现自己的权限范围内的内容,避免在界面上给用户带来困扰,让用户专注于分内之事

二、前端权限控制思路

2.1 菜单的控制

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

2.2 界面的控制

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

2.3 按钮的控制

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

2.4 请求和响应的控制

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

三、实现步骤

3.1 权限菜单栏控制

用户登录后服务器返回一个数据,这个数据有菜单列表和token,我们把这个数据放到vuex中,然后主页根据vuex中的数据进行菜单列表的渲染

问题:刷新界面vuex数据消失,菜单栏消失
解决:将数据存储在sessionStorage中,并让其和vuex中的数据保持同步

3.2 界面的控制

登录成功后,将token数据存储在sessionStorage中,判断其是否登录

  1. 路由导航守卫
  router.beforeEach((to,from,next)=>{
      if(to.path === '/login'){
          next()
      }else{
          const token = sessionStorage.getItem('token');
          if(!token){
              next('/login')
          }else{
              next()
          }
      }
  })

问题:这样用户在登录之后就可以访问其他界面了,但如果用户A登录之后只能访问a页面,不能访问页面,但是这个时候还是可以通过地址栏输入进入到b页面
解决:当然我们也可以设置路由导航守卫,但是如果有多个页面,设置会非常不方便,并且对于A来说,它是不用访问b页面的,这时候我们何不对A不显示b页面,这个时候我们就用到了动态路由

  1. 动态路由
    根据当前用户所拥有的权限数据来动态添加所需要的路由
  2. 先定义好所有的路由规则
  const userRule = {path: '/users',components: Users }
  const roleRule = {path: '/roles',components: Roles }
  const goodRule = {path: '/goods',components: GoodList }
  1. 登录成功之后动态添加路由,注意这个initDynamicRoutes的方法需要暴露出在登录页面调用
exprot function initDynamicRoutes(){
    //根据二级权限,对路由规则进行动态的添加
    const currentRoutes = router.options.routers;
    const rightList = stroe.state.rightList;
    rightList.forEach(item => {
        item.children.forEach(item =>{
            const temp = ruleMapping[item.path];
            temp.meta = item.rights;
            currentRoutes[2].children.push(temp);//设置路由元信息-添加动态路由
        })
    })
}

这样当用户A在地址栏输入自己不能访问的路由时,则不会跳转到该页面,会跳转到404页面
问题:如果我们重新刷新的话,动态路由就会消失,动态路由是登录成功之后才会调用的,刷新的时候并没有调用,所以动态路由并没有添加上
解决:可以在app.vue中的created中调用动态路由的方法

export default {
    name:'app',
    created() {
        initDynamicRoutes();
    },
}

3.3 按钮的控制

虽然用户可以看到某些界面了,但是这个界面的一些按钮该用户可能是没有权限的。因此,我们需要对组件中的一些按钮进行控制,用户不具备权限的按钮就隐藏或者禁用,而在这块的实现中个,可以把该逻辑放在自定义指令中
比如,我们可以根据后端返回的数据right来判断用户有什么权限

right:['view']

添加自定义指令
v-permission="{action:'add',effect:'disavled'}"
在permission.js中

import Vue from 'vue';
    import router from '@/router.js'
    Vue.directives('promission',{
        inserted: function(el,binding){
            const action = binding.value.action;//对应指令中的'add'
            const currentRight = router.currentRight.meta;//获取当前路由元信息,meta里存的是当前路由的权限信息
            if(currentRight){
                if(currentRight.indexOf(action) == -1){
                    //不具备权限
                    const type = binding.value.effect;
                    if(type="disabled"){
                        el.disabled = true;
                        el.classList.add('is-disabled');//禁用元素
                    }else{
                        el.parentNode.removeChild(el);//移除元素
                    }
                }
            }
        }
    })

3.4 请求和响应的控制

(1)请求控制

  • 除了登录请求都要带上token,这样服务器才可以鉴别你的身份
    这块使用的是axios的请求拦截器设置
  axios.interceptors.request.use(function(req){
        const currentUrl = req.url;
        if(currentUrl !='login'){ //不是登录的时候,在请求头中加入token数据
            res.headers.Authorization = sessionStorage.getItem('token');
        }
    })
  • 如果发出了非权限内的请求,应该直接在前端范围内阻止,但是通过f12 调试把按钮改为可点击,如果不对这个请求进行处理,那么这个请求会发送出去

  import axios from 'axios'
  import Vue from 'vue'
  import router from '../router'
  //配置请求的根路径
  const actionMapping = {
      get:'view',
      post:'add',
      put:'edit',
      delete:'delete'
  }

  axios.interceptors.request.use(function(req){
    const currentUrl = req.url;
    if(currentUrl !='login'){ //不是登录的时候,在请求头中加入token数据
        res.headers.Authorization = sessionStorage.getItem('token');
        // 当前模块中具备的权限
        //查看 get 请求
        //增加 post 请求
        //修改 put 请求
        //删除 delete 请求
        const method = req.method;
        //根据请求,得到是哪种操作
        const action = actionMapping[method];
        //判断action是否存在当前路由的权限中
        const rights = router.currentRoute.meta;
        if(right && rights.indexOf(action) == -1){
            //没有权限
            alert('没有权限');
            return Promise.reject(new Error('没有权限'))
        }

    }
})

(2)响应控制
得到了服务器返回的状态码是401,代表token超时或者被篡改了,此时应该强制跳转到登录界面

 axios.interceptors.response.use(function(req){
    if(res.data.meta.status == 401){
        router.push('/login')
        sessionStorage.clear();
        window.location.reload();
    }
    return res;
 })

四、小结

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

4.1 菜单的控制

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

4.2 界面的控制

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

4.3 按钮控制

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

4.4 请求和响应控制

  • 请求拦截器和响应拦截器的使用
  • 请求方式约定的 restful
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容