首頁  >  文章  >  web前端  >  Vue2.0怎麼操作使用者權限

Vue2.0怎麼操作使用者權限

php中世界最好的语言
php中世界最好的语言原創
2018-04-14 13:54:291506瀏覽

這次帶給大家Vue2.0怎樣操作使用者權限,Vue2.0操作使用者權限的注意事項有哪些,以下就是實戰案例,一起來看一下。

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

安裝

版本需求

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」參數用於產生模擬數據。

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

node.js實作web終端機操作多用戶

在微信小程式裡新增彈出對話框

以上是Vue2.0怎麼操作使用者權限的詳細內容。更多資訊請關注PHP中文網其他相關文章!

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