若依权限系统分析
一:故事背景
最近在研究权限系统。秉承着站在巨人的肩膀上的原则,去学习了若依的权限项目。这里选用的是前后端分离版。去研究了界面的控制和页面内元素的控制。查看了其具体的实现方法,将其总结到这里。
二:具体权限控制
若依权限控制主要分为两大部分,第一部分是页面的控制,第二部分是页面上具体元素的控制。
1.在若依的系统中,菜单管理内可以看到所有元素。元素分为两种。一种是页面,另外一种是元素是页面内的具体元素。
2.无论是哪种元素,我们都可以自定义对应的 权限标识,通过权限表示来表示用户有没有具体的权限。
2.1 页面权限控制
页面权限控制是通过动态路由的方式进行的配置,我们从前端到后端,整个流程看一下其实现动态路由的整个过程
1.src目录下的permission文件,这里代码意思是VueRouter中的全局前置守卫,用于在路由切换前执行一些逻辑操作。
首先判断用户已经登录,有对应的token,然后去拉取用户的信息,拉去完之后去触发 Vuex的GenerateRoutes方法,去后端拉取具体的路由信息2.上文所述的 Vuex的GenerateRoutes方法 定义在 src.store.modules.permission.js 文件内,注意和上面的文件区分,二者目录不同
这部分代码,去后端查询了用户具有的路由权限等信息,将生成的可访问的路由配置作为 Promise 的结果返回。
返回之后,第一部分的代码将可访问的路由动态的配置到了 Vue Router 中。
3.下面是请求路由数据返回的数据结构
{ "msg": "操作成功", "code": 200, "data": [ { "name": "System", "path": "/system", "hidden": false, "redirect": "noRedirect", "component": "Layout", "alwaysShow": true, "meta": { "title": "系统管理", "icon": "system", "noCache": false, "link": null }, "children": [ { "name": "User", "path": "user", "hidden": false, "component": "system/user/index", "meta": { "title": "用户管理", "icon": "user", "noCache": false, "link": null } }, { "name": "Role", "path": "role", "hidden": false, "component": "system/role/index", "meta": { "title": "角色管理", "icon": "peoples", "noCache": false, "link": null } }, { "name": "Menu", "path": "menu", "hidden": false, "component": "system/menu/index", "meta": { "title": "菜单管理", "icon": "tree-table", "noCache": false, "link": null } }, { "name": "Dept", "path": "dept", "hidden": false, "component": "system/dept/index", "meta": { "title": "部门管理", "icon": "tree", "noCache": false, "link": null } }, { "name": "Post", "path": "post", "hidden": false, "component": "system/post/index", "meta": { "title": "岗位管理", "icon": "post", "noCache": false, "link": null } }, { "name": "Dict", "path": "dict", "hidden": false, "component": "system/dict/index", "meta": { "title": "字典管理", "icon": "dict", "noCache": false, "link": null } } ] } ] }
2.2 页面元素权限控制
页面元素控制,通过权限id进行的相应控制,在若依的权限系统中,定义了v-hasPermi 和 v-hasRole 两个Vue指令。分别用来控制操作权限和角色权限处理。在用户登录的时候,会调用getInfo接口,获取用户对应的权限id的字段,并且将其存储到vuex的状态中,可以在其他的组件中通过Vuex的getters获取具体的信息getUserInfo获得的具体的用户权限信息页面鉴权的时候直接通过 v-hasPermi 来进行对应的鉴权,如果该用户返回了对应的权限id,该用户对此控件就有具体的操作权限
三:实现前端鉴权
基于若依,我自己封装了一个前端鉴权方式。通过调用若依权限接口,二次开发接口的方式,实现了路由鉴权
此实现主要涉及到以下几方面:
1.封装js与权限系统交互。
2.利用vuex实现状态管理,管理用户可访问的路由路径
3.全局路由守卫,实现鉴权
3.1 封装js与权限交互
我这里封装了两种js与权限系统交互,一种是利用axios、一种是利用uni-app框架的uni-request进行访问。
3.1.1 uni-app自带uni-request与权限交互
//使用uni.request方式发送http请求 //登陆获取token const AUTH_LOGIN_URL= "http://localhost:8000/auth/login" //获取用户权限 const AUTH_USERINFO_URL="http://localhost:8000/auth/getResource" //返回token值等数据 const state = { //token值 token:'', //路由 routes:[], //权限id permissions:[] } //登陆获取token,以及用户权限 export function getAuth(data) { return new Promise((resolve, reject) => { sendRequest(AUTH_LOGIN_URL, 'post', data) .then(response => { // 请求token返回值 console.log("response",response); state.token = response.data.token; console.log("token",data); getUserInfo(state.token).then(userRespone=>{ state.permissions = userRespone.data.permissions; state.routes = userRespone.data.component; resolve(state) }) }) .catch(error => { // 请求发生错误,进行错误处理 console.error(error); reject(error); // 将错误传递给调用者 }); }); } //通过token获取用户权限 function getUserInfo(token){ return new Promise((resolve, reject) => { sendRequest(AUTH_USERINFO_URL, 'get', null,token) .then(response => { // 请求token返回值 console.log("请求路由权限成功",response) resolve(response); // 将结果传递给调用者 }) .catch(error => { // 请求发生错误,进行错误处理 console.error("发生错误",error); reject(error); // 将错误传递给调用者 }); }); } function sendRequest(url, method, data, token) { return new Promise(function(resolve, reject){ uni.request({ url: url, method: method, data: data, header: { 'Authorization': 'Bearer ' + token // 设置 Authorization 头部 } }).then(responses=>{ // 异常 if (responses[0]) { reject({message : "网络超时"}); } else { resolve (responses[1]); } }).catch(error =>{ reject(error); }); }) }
这个js专门与权限系统交互,查询用户的token,用户的权限等等。我这里主要定义了3个属性。token、routers、permissions,使用这三个实现对路由和页面元素的鉴权。当然我们还可以增加接口鉴权,等等
3.2 vux状态管理
上文所述查询到对应的权限之后,我们可以通过使用vuex将状态保存,利于我们项目之中进行使用。
3.2.1 自定义状态
通过自定义状态,定义应该交由vuex进行保存的状态
import { getAuth } from "./uniCallAuth" const permission ={ //各种状态 state: { token:'', routes: [], permissions:[] }, mutations:{ //获得权限token AUTH_TOKEN: (state,token) =>{ state.token = token }, AUTH_PERMISSIONS: (state,permissions) =>{ state.permissions = permissions }, AUTH_ROUTERS: (state,routes) =>{ state.routes = routes } }, actions:{ getUserAuth({commit},data){ return new Promise(resolve => { getAuth(data).then(userData=>{ console.log("userData",userData) //提交相关的状态 commit('AUTH_TOKEN',userData.token); commit('AUTH_ROUTERS',userData.routes); commit('AUTH_PERMISSIONS',userData.permissions); console.log("用户状态已提交"); resolve(); }) }) } } } export default permission
3.2.2 在vuex的store配置内添加我们新增的状态管理
具体使用状态的方法;
3.3 全局路由守卫鉴权
在用户登陆时,通过调用权限进行鉴权,鉴权结束后,vuex就存储了相应的权限状态,我们通过定义方法,对全局路由进行鉴权
Vue.prototype.$bus = new Vue(); Vue.prototype.$bus.$on('uniNavigateTo', options => { // 全局路由守卫的具体逻辑 console.log("全局路由守卫起作用",options); let newString //截取url防止get请求拼参 if(options.url.includes('?')){ newString = options.url.substring(0, options.url.indexOf('?')); }else{ newString = options.url; } //登陆页面直接跳转 if(newString == '/pages/views/user/phoneLogin' || newString =='/pages/views/seller/NotLogin'){ console.log("普通页面直接跳转"); uni.navigateTo(options); return; } console.log("newString",newString); //判断是否具有页面跳转权限 if(! checkRouter(newString,store.state.permission.routes)){ // uni.showToast(String("没有权限")); console.log("无法跳转uniNavigateTo",store.state.permission.routes); return; } console.log("应该跳转页面"); uni.navigateTo(options); }); Vue.prototype.$bus.$on('uniSwitchTab', options => { console.log("底部全局路由守卫起作用",options); //截取url防止get请求拼参 let newString //截取url防止get请求拼参 if(options.url.includes('?')){ newString = options.url.substring(0, options.url.indexOf('?')); }else{ newString = options.url; } //判断是否具有页面跳转权限 if(! checkRouter(newString,store.state.permission.routes)){ // uni.showToast(String("没有权限")); console.log("无法跳转uniSwitchTab"); return; } console.log("应该跳转页面"); uni.switchTab(options); });
四:总结提升
本文通过研究若依系统的权限,实现了自己项目的鉴权,并且在若依的基础上进行了对应的二次开发。理清了前端鉴权的方式,并且进行了实现。大家如果需要前端鉴权,可以参考。