Home  >  Article  >  Web Front-end  >  About front-end user permission control in Vue-Access-Control (detailed tutorial)

About front-end user permission control in Vue-Access-Control (detailed tutorial)

亚连
亚连Original
2018-06-22 18:28:401203browse

Vue-Access-Control is a front-end user permission control solution based on Vue/Vue-Router/axios. This article mainly introduces Vue-Access-Control: a front-end user permission control solution. Friends who need it can refer to it

Vue-Access-Control is a front-end based on Vue/Vue-Router/axios The user permission control solution enables developers to achieve user permission control at any granularity by controlling the routing, view, and request levels.

Overall idea

At the beginning of the session, first initialize a Vue instance with only login routing, and direct the routing in the root component created hook Go to the login page. After the user logs in successfully, the front end gets the user token, sets the axios instance to uniformly add {"Authorization":token} to the request headers to implement user authentication, and then obtains the current user's permission data, which mainly includes routing permissions and resource permissions. Afterwards, routes are dynamically added, menus are generated, permission instructions and global permission verification methods are implemented, and request interceptors are added for the axios instance, thus completing the permission control initialization. After dynamically loading the route, the routing component will be loaded and rendered, and then the front-end interface will be displayed.

In order to solve the problem of browser refresh route reset, after getting the token, it must be saved to sessionStorage. The created hook of the root component is responsible for checking whether the token already exists locally. If so, use the token directly without logging in. Obtain permissions and initialize. If the token is valid and the current route has access, the routing component will be loaded and displayed correctly; if the current route does not have access, it will jump to 404 according to the routing settings; if the token is invalid, the backend should return a 4xx status code, and the frontend Unifiedly add an error interceptor to the axios instance, perform an exit operation when encountering a 4xx status code, clear the sessionStorage data and jump to the login page, allowing the user to log in again.

Minimum Dependency Principle

Vue-Access-Control is positioned as a single-domain solution, in addition to Vue/Vue-Router/axios There are no other dependencies. In theory, it can be applied to any Vue project with permission control requirements without any obstacles. The project is developed and built based on the webpack template. Most new projects can continue to be developed directly based on the checked-out code. It should be noted that the additional Element-UI and CryptoJS introduced in the project are only used to develop the demonstration interface. They are not necessary and have nothing to do with permission control. You can choose them by yourself in the project application.

Directory structure

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

Data format convention

Routing permissions The data must be an object array in the following format. Two routes with the same id and parent_id have a superior-subordinate relationship. If you want to use routing data in a custom format, you need to modify the relevant implementation of routing control. For details, see Routing Control Data Format Convention

[
 {
  "id": "1",
  "name": "菜单1",
  "parent_id": null,
  "route": "route1"
 },
 {
  "id": "2",
  "name": "菜单1-1",
  "parent_id": "1",
  "route": "route2"
 }
 ] 

Resource permission data must be an object array in the following format. Each object represents a RESTful request and supports URLs with parameters. For specific format instructions, see Request Control

[
 {
  "id": "2c9180895e172348015e1740805d000d",
  "name": "账号-获取",
  "url": "/accounts",
  "method": "GET"
 },
 {
  "id": "2c9180895e172348015e1740c30f000e",
  "name": "账号-删除",
  "url": "/account/**",
  "method": "DELETE"
 }
] 

Routing Control

Routing control includes two parts: dynamic registration of routes and dynamic generation of menus.

Dynamic registration route

The initially instantiated route only includes two paths: login and 404. We expect the complete route to look like this:

[{
 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'
}]

Then you need to obtain the home page and its sub-routes. The idea is to store the complete routing data of the entire project locally in advance, and then filter the complete routes according to user permissions.

The implementation idea of ​​filtering is to first process the routing data returned by the backend into the following hash structure:

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

Then traverse the local complete route, and splice the path into the above structure in the loop. key format, you can use hashMenus[route] to determine whether the route matches. For specific implementation, see the getRoutes() method in the App.vue file.

If the routing permission data returned by the backend is different from the agreement, you need to implement the filtering logic yourself. As long as you can get the actual available routing data, you can finally use the addRoutes() method to dynamically add them to the routing instance. , please note that the fuzzy matching of the 404 page must be placed at the end.

Dynamic Menu

Routing data can be used directly to generate the navigation menu, but the routing data is obtained in the root component, and the navigation menu exists In the index.vue component, obviously we need to share the menu data in some way. There are many methods. Generally speaking, the first thing that comes to mind is Vuex, but the menu data will not change during the entire user session. This is not Vuex's The best usage scenario, and in order to minimize unnecessary dependencies, the simplest and most direct method is used here, hanging the menu data on the root component data.menuData, and using this.$parent.menuData to obtain it on the home page.

In addition, the navigation menu is likely to have the need to add column icons. This can be achieved by adding meta data to the route. For example, if the icon class or unicode is stored in the route meta, the meta can be accessed in the template. Data used to generate icon labels.

在多角色系统中可能遇到的一个问题是,不同角色有一个名字相同但功能不同的路由,比如说系统管理员和企业管理员都有”账号管理”这个路由,但他们的操作权限和目标不同,实际上是两个完全不同的界面,而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: 任意

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

Solve the problem of the input box being blocked by the input method

How to implement the Chinese map in vue vuex axios echarts

In How to switch between styles in vue

How to make pictures larger in JavaScript

The above is the detailed content of About front-end user permission control in Vue-Access-Control (detailed tutorial). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn