首頁  >  文章  >  web前端  >  在Vue2.0中實現使用者權限控制

在Vue2.0中實現使用者權限控制

亚连
亚连原創
2018-06-06 14:14:171792瀏覽

這篇文章主要介紹了Vue2.0用戶權限控制解決方案的範例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧

Vue-Access-Control是一套基於Vue/Vue-Router/axios 實現的前端用戶權限控制解決方案,透過對路由、視圖、請求三個層面的控制,使開發者可以實現任意顆粒度的使用者權限控制。

安裝

版本要求

  1. #Vue 2.0x

Vue 2.0x

Vue-router 3.x

取得

#git:git clone https://github.com/tower1229/Vue-Access-Control.git

npm:npm i vue-access-control

運行

//开发
npm run dev

//构建
npm build

概述

整體想法

會話開始之初,先初始化一個只有登入路由的Vue實例,在根元件created鉤子裡將路由導向登入頁,使用者登入成功後前端拿到使用者token,設定axios實例統一為請求headers添加{"Authorization":token}實現用戶鑑權,然後獲取當前用戶的權限數據,主要包括路由權限和資源權限,之後動態添加路由,生成菜單,實現權限指令和全域權限驗證方法,並為axios實例新增請求攔截器,至此完成權限控制初始化。動態載入路由後,路由元件將隨之載入並渲染,而後展現前端介面。

為解決瀏覽器刷新路由重置的問題,拿到token後要將其保存到sessionStorage,根組件的created鉤子負責檢查本地是否已有token,如果有則無需登錄直接用該token取得權限並初始化,如果token有效且當前路由有權訪問,將加載路由組件並正確展現;若當前路由無權訪問將按路由設定跳轉404;如果token失效,後端應返回4xx狀態碼,前端統一為axios實例新增錯誤攔截器,遇到4xx狀態碼執行退出操作,清除sessionStorage資料並跳到登入頁,讓使用者重新登入。

最小依賴原則

Vue-Access-Control的定位是單一領域解決方案,除了Vue/Vue-Router/axios之外沒有其他依賴,理論上可以無障礙的應用到任何有權限控制需求的Vue專案中,專案基於webpack 範本開發構建,大多數新專案可以直接基於檢出程式碼繼續開發。需要說明的是,專案額外引入的Element-UI和 CryptoJS僅用於開發演示介面,他們不是必須且與權限控制毫無關係,專案應用中可以自行取捨。

目錄結構

src/
 |-- api/         //接口文件
 |   |-- index.js       //输出通用axios实例
 |   |-- account.js      //按业务模块组织的接口文件,所有接口都引用./index提供的axios实例
 |-- assets/
 |-- components/
 |-- router/
 |   |-- fullpath.js     //完整路由数据,用于匹配用户的路由权限得到实际路由
 |   `-- index.js      //输出基础路由实例
 |-- views/
 |-- App.vue
 ·-- main.js
資料格式約定

#路由權限資料必須是以下格式的物件數組,id和parent_id相同的兩個路由具有上下級關係,如果希望使用自訂格式的路由數據,則需要修改路由控制的相關實現,詳見路由控制

[
  {
   "id": "1",
   "name": "菜单1",
   "parent_id": null,
   "route": "route1"
  },
  {
   "id": "2",
   "name": "菜单1-1",
   "parent_id": "1",
   "route": "route2"
  }
 ]
資源權限資料必須是如下格式的物件數組,每個物件代表一個RESTful請求,支援帶參數的url,具體格式說明請參閱請求控制
 [
  {
   "id": "2c9180895e172348015e1740805d000d",
   "name": "账号-获取",
   "url": "/accounts",
   "method": "GET"
  },
  {
   "id": "2c9180895e172348015e1740c30f000e",
   "name": "账号-删除",
   "url": "/account/**",
   "method": "DELETE"
  }
]

#路由控制

路由控制包括動態註冊路由和動態生成菜單兩部分。

動態註冊路由

最初實例化的路由只包含登入和404兩個路徑,我們期待完整的路由是這樣的:

[{
 path: '/login',
 name: 'login',
 component: (resolve) => require(['../views/login.vue'], resolve)
}, {
 path: '/404',
 name: '404',
 component: (resolve) => require(['../views/common/404.vue'], resolve)
}, {
 path: '/',
 name: '首页',
 component: (resolve) => require(['../views/index.vue'], resolve),
 children: [{
  path: '/route1',
  name: '栏目1',
  meta: {
   icon: 'icon-channel1'
  },
  component: (resolve) => require(['../views/view1.vue'], resolve)
 }, {
  path: '/route2',
  name: '栏目2',
  meta: {
   icon: 'ico-channel2'
  },
  component: (resolve) => require(['../views/view2.vue'], resolve),
  children: [{
   path: 'child2-1',
   name: '子栏目2-1',
   meta: {
    
   },
   component: (resolve) => require(['../views/route2-1.vue'], resolve)
  }]
 }]
}, {
 path: '*',
 redirect: '/404'
}]
那麼接下來就需要取得首頁以及其子路由們,思路是事先在本地存一份整個專案的完整路由數據,然後根據使用者權限對完整路由進行篩選。

篩選的實作想法是先將後端回傳的路由資料處理成如下雜湊結構:

let hashMenus = {
  "/route1":true,
  "/route1/route1-1":true,
  "/route1/route1-2":true,
  "/route2":true,
  ...
}

然後遍歷本地完整路由,在循環中將路徑拼接成上述結構中的key格式,透過hashMenus[route]就可以判斷路由是否匹配,具體實作請參閱App.vue檔案中的getRoutes()方法。

如果後端回傳的路由權限資料與約定不同,就需要自行實作篩選邏輯,只要能得到實際可用的路由資料就可以,最終使用addRoutes()方法將他們動態加入到路由實例中,注意404頁面的模糊配對一定要放在最後。 #########動態選單#########路由資料可以直接用來產生導覽選單,但路由資料是在根元件中得到的,導航選單存在於index.vue元件中,顯然我們需要以某種方式共享選單數據,方法有很多,一般來說首先想到的是Vuex,但選單數據在整個用戶會話過程中不會發生改變,這並不是Vuex的最佳使用場景,而且為了盡量減少不必要的依賴,這裡用了最簡單直接的方法,把選單資料掛在根元件data.menuData上,在首頁用this.$parent.menuData取得。 ######另外,導航選單很可能會有添加欄位圖示的需求,這可以透過在路由中加入meta資料實現,例如將圖示class或unicode存到路由meta裡,模板就可以存取到meta數據,用來產生圖標標籤。 ###

在多角色系统中可能遇到的一个问题是,不同角色有一个名字相同但功能不同的路由,比如说系统管理员和企业管理员都有”账号管理”这个路由,但他们的操作权限和目标不同,实际上是两个完全不同的界面,而Vue不允许多个路由同名,因此路由的name必须做区分,但把区分后的name显示在前端菜单上会很不美观,为了让不同角色可以享有同一个菜单名称,我们只要将这两个路由的meta.name都设置成”账号管理”,在模板循环时优先使用meta.name就可以了。

菜单的具体实现可以参考views/index.vue。

视图控制

视图控制的目标是根据当前用户权限决定界面元素显示与否,典型场景是对各种操作按钮的显示控制。实现视图控制的本质是实现一个权限验证方法,输入请求权限,输出是否获准。然后配合v-if或jsx或自定义指令就能灵活实现各种视图控制。

全局验证方法

验证方法的的实现本身很简单,无非是根据后端给出的资源权限做判断,重点在于优化方法的输入输出,提升易用性,经过实践总结最终使用的方案是,将权限跟请求同时维护,验证方法接收请求对象数组为参数,返回是否具有权限的布尔值。

请求对象格式:

//获取账户列表
const request = {
 p: ['get,/accounts'],
 r: params => {
  return instance.get(`/accounts`, {params})
 }
}

权限验证方法$_has()的调用格式:

v-if="$_has([request])"

权限验证方法的具体实现见App.vue中Vue.prototype.$_has方法。

将权限验证方法全局混入,就可以在项目中很容易的配合v-if实现元素显示控制,这种方式的优点在于灵活,除了可以校验权限外,还可以在判断表达式中加入运行时状态做更多样性的判断,而且可以充分利用v-if响应数据变化的特点,实现动态视图控制。

具体实现细节参考基于Vue实现后台系统权限控制中的相关章节。

自定义指令

v-if的响应特性是把双刃剑,因为判断表达式在运行过程中会频繁触发,但实际上在一个用户会话周期内其权限并不会发生变化,因此如果只需要校验权限的话,用v-if会产生大量不必要的运算,这种情况只需在视图载入时校验一次即可,可以通过自定义指令实现:

//权限指令
Vue.directive('has', {
 bind: function(el, binding) {
  if (!Vue.prototype.$_has(binding.value)) {
   el.parentNode.removeChild(el);
  }
 }
});

自定义指令内部仍然是调用全局验证方法,但优点在于只会在元素初始化时执行一次,多数情况下都应该使用自定义指令实现视图控制。

请求控制

请求控制是利用axios拦截器实现的,目的是将越权请求在前端拦截掉,原理是在请求拦截器中判断本次请求是否符合用户权限,以决定是否拦截。

普通请求的判断很容易,遍历后端返回的的资源权限格式,直接判断request.method和request.url是否吻合就可以了,对于带参数的url需要使用通配符,这里需要根据项目需求前后端协商一致,约定好通配符格式后,拦截器中要先将带参数的url处理成约定格式,再判断权限,方案中已经实现了以下两种通配符格式:

1. 格式:/resources/:id
   示例:/resources/1
   url: /resources/**
   解释:一个名词后跟一个参数,参数通常表示名词的id  

2. 格式:/store/:id/member
   示例:/store/1/member
   url:/store/*/member

 解释:两个名词之间夹带一个参数,参数通常表示第一个名词的id
对于第一种格式需要注意的是,如果你要发起一个url为"/aaa/bbb"的请求,默认会被处理成"/aaa/**"进行权限校验,如果这里的”bbb”并不是参数而是url的一部分,那么你需要将url改成"/aaa/bbb/",在最后加一个”/“表示该url不需要转化格式。

拦截器的具体实现见App.vue中的setInterceptor()方法。

如果你的项目还需要其他的通配符格式,只需要在拦截器中实现对应的检测和转化方法就可以了。

演示及说明

演示说明:

DEMO项目中演示了动态菜单、动态路由、按钮权限、请求拦截。

演示项目后端由rap2生成mock数据,登录请求通常应该是POST方式,但因为rap2的编程模式无法获取到非GET的请求参数,因此只能用GET方式登录,实际项目中不建议仿效;

另外登录后获取权限的接口本来不需要携带额外参数,后端可以根据请求头携带的token信息实现用户鉴权,但因为rap2的编程模式获取不到headers数据,因此只能增加一个”Authorization”参数用于生成模拟数据。

测试账号:

1. username: root
   password: 任一
2. username: client
   password: 任意

上面是我整理給大家的,希望今後會對大家有幫助。

相關文章:

透過angularJS中的radio實作單一項目二選一的使用方法(詳細教學)

在vue中全面解讀cli(詳細教學)

如何透過js實作動態改變radio狀態(詳細教學)

#

以上是在Vue2.0中實現使用者權限控制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn