补充一下模块源代码html
关联上一篇Vue 前端应用实现RBAC权限控制的一种方式,这篇写的比较懒,哈哈,仍是谢谢支持的朋友。前端
承蒙李杨的邀请,就写了这篇文章,平时写的很少,失误之处,请你们多多包涵。vue
由于工做的缘由要找一个管理端模板,用于开发一个网银系统的前端界面骨架,我就找到了d2-admin,看着十分对胃口,接着我就想着先作一个先后端分离的demo,来看一下d2-admin模板是否知足咱们项目的需求,那么一个权限管理demo我以为很适合拿来作实验,就此我作了jiiiiiin-security权限系统项目,如下是部分功能截图:java
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
在说咱们前端为何要实现权限控制以前,你们势必要了解一下咱们要实现的东西的本质是什么,下面简单引用两句介绍:linux
更多内容,请你们不熟悉的势必自行google;github
我认为先后端是相辅相成的,因此要作好前端的权限控制,若是能提早了解后端的权限分配规则和数据结构是可以更好的进行相互配合的,固然若是彻底不理会后台的权限划分,硬性来作上面的两个需求也是能实现的,只是不掌握全局,就很难理解这样作的意义何在,因此建议你们在考虑这个问题的时候(这里指前端同窗),仍是要大概去看看RBAC的概念,属性经典的表结构,从而属性后台权限分别的业务规则。ajax
“权限管理”通常你们的印象中都属于后端的责任,可是这两年随着SPA应用的兴起,不少应用都采用了先后端分离的方式进行开发,可是纯前端的开发方式就致使不少之前由后端模板语言硬件解决的问题,如今势必要从新造一次轮子,而这个时候前端我认为是配合后端对应语言的安全框架根据自身的业务须要来实现,在这里就说说咱们的需求:正则表达式
以上二、三、4点在先后端未曾分离的时候,这些事情都是由后类html模板语言(如传统的java中的jsp)所包办的,相似这样:
<html>
<sec:authorize access="hasRole('supervisor')">
This content will only be visible to users who have
the "supervisor" authority in their list of <tt>GrantedAuthority</tt>s.
</sec:authorize>
</html>
复制代码
v-access
指令,对比声明的接口或资源别是否已经受权router
进行导航的beforeEach
前置钩子中判断当前用户所请求的页面是否在以上两个集合之中,若是是则放行,若是不是,则通知插件调用方,让其本身处理失败的状况下面是代码实现:
/** * RBAC权限控制模块 */
import _ from 'lodash';
let _onPathCheckFail
let _publicPaths = []
let _authorizedPaths = []
/** * 是不是【超级管理员】 * 若是登陆用户是这个`角色`,那么就无需进行各类受权控制检测 * @type {boolean} * @private */
let _superAdminStatus = false
const _compare = function(rule, path) {
let temp = false
if (_.isRegExp(rule)) {
temp = rule.test(path)
} else {
temp = _.isEqual(path, rule)
}
return temp
}
/** * 检测登陆用户是否具备访问对应页面的权限 * 1.校验是否登陆 * 2.校验带访问的页面是否在`loginStateCheck#authorizedPaths`受权`paths`集合中 * @param to * @param from * @param next * @private */
const _rbacPathCheck = function(to, from, next) {
if (_superAdminStatus) {
next();
return;
}
try {
// 默认认为全部资源都须要进行权限控制
let isAllow = false
const path = to.path;
// 先检测公共页面集合
const publicPathsLength = _publicPaths.length
for (let i = publicPathsLength; i--;) {
const rule = _publicPaths[i];
isAllow = _compare(rule, path)
if (isAllow) {
break;
}
}
// 非公共页面 && 已经登陆
if (!isAllow && this.isLogin()) {
// 检测已受权页面集合
const authorizedPathsLength = _authorizedPaths.length;
for (let i = authorizedPathsLength; i--;) {
const rule = _authorizedPaths[i];
isAllow = _compare(rule, path);
if (isAllow) {
break;
}
}
}
if (isAllow) {
next();
} else {
if (_.isFunction(_onPathCheckFail)) {
if (_debug) {
console.error(`[v+] RBAC模块检测:用户无权访问【${path}】,回调onPathCheckFail钩子`);
}
this::_onPathCheckFail(to, from, next);
} else {
next(new Error('check_authorize_paths_fail'));
}
}
} catch (e) {
if (_debug) {
console.error(`[v+] RBAC模块检测出错: ${e.message}`);
}
if (_.isFunction(_errorHandler)) {
this::_errorHandler(e)
}
}
};
const rbacModel = {
/** * 【可选】有些系统存在一个超级用户角色,其能够访问任何资源、页面,故若是设置,针对这个登陆用户将不会作任何权限校验,以便节省前端资源 * @param status */
rabcUpdateSuperAdminStatus(status) {
_superAdminStatus = status;
this.cacheSaveToSessionStore('AUTHORIZED_SUPER_ADMIN_STATUS', _superAdminStatus)
},
/** * 添加受权路径集合 * 如:登陆完成以后,将用户被受权能够访问的页面`paths`添加到`LoginStateCheck#authorizedPaths`中 * @param paths */
rabcAddAuthorizedPaths(paths) {
this::rbacModel.rabcUpdateAuthorizedPaths(_.concat(_authorizedPaths, paths))
},
/** * 更新受权路径集合 * @param paths */
rabcUpdateAuthorizedPaths(paths) {
_authorizedPaths = [...new Set(paths)]
this.cacheSaveToSessionStore('AUTHORIZED_PATHS', _authorizedPaths)
},
/** * 更新公共路径集合 * @param paths */
rabcUpdatePublicPaths(paths) {
_publicPaths = [...new Set(paths)];
this.cacheSaveToSessionStore('PUBLIC_PATHS', _publicPaths)
},
/** * 添加公共路径集合 * @param paths */
rabcAddPublicPaths(paths) {
this::rbacModel.rabcUpdatePublicPaths(_.concat(_publicPaths, paths))
},
install(Vue, {
/** * [*] 系统公共路由path路径集合,便可以让任何人访问的页面路径 * {Array<Object>} * <p> * 好比登陆页面的path,由于登陆以前咱们是没法判断用户是否能够访问某个页面的,故须要这个配置,固然若是须要这个配置也能够在初始化插件以前从服务器端获取,这样先后端动态性就更高,可是通常没有这种需求:) * <p> * 数组中的item,能够是一个**正则表达式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也能够是一个字符串 * <p> * 匹配规则:若是在`LoginStateCheck#publicPaths`**系统公共路由path路径集合**中,那么就直接跳过权限校验 */
publicPaths = [],
/** * [*] 登陆用户拥有访问权限的路由path路径集合 * {Array<Object>} * <p> * 数组中的item,能够是一个**正则表达式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也能够是一个字符串 * <p> * 匹配规则:若是在`LoginStateCheck#authorizedPaths`**须要身份认证规则集**中,那么就须要查看用户是否登陆,若是没有登陆就拒绝访问 */
authorizedPaths = [],
/** * [*] `$vp::onPathCheckFail(to, from, next)` * <p> * 访问前端页面时权限检查失败时被回调 */
onPathCheckFail = null,
} = {}) {
_onPathCheckFail = onPathCheckFail;
router.beforeEach((to, from, next) => {
this::_rbacPathCheck(to, from, next);
});
}
};
export default rbacModel;
复制代码
这里解释一下:
整个代码最终导出了一个普通的json对象,做为vue-viewplus的一个自定义模块,将会被mixin到其插件内部做为一个自定义模块:
// 应用入口mian.js
import Vue from 'vue'
import router from './router'
import ViewPlus from 'vue-viewplus'
import viewPlusOptions from '@/plugin/vue-viewplus'
import rbacModule from '@/plugin/vue-viewplus/rbac.js'
Vue.use(ViewPlus, viewPlusOptions)
ViewPlus.mixin(Vue, rbacModule, {
moduleName: '自定义RBAC',
router,
publicPaths: ['/login'],
onPathCheckFail(to, from, next) {
NProgress.done()
const title = to.meta.title
this.dialog(`您无权访问【${_.isNil(title) ? to.path : title}】页面`)
.then(() => {
// 没有登陆的时候跳转到登陆界面
// 携带上登录成功以后须要跳转的页面完整路径
next(false)
})
}
})
复制代码
你们若是没有使用或者不想使用这个插件(vue-viewplus也无所谓,这里只要知道,导出的这个对象的install会在应用入口被调用,并传入几个install方法几个必须的参数:
这样咱们就能在初始化函数中缓存应用公共页面paths列表,注册路由钩子,监听路由变化。
这里我使用这个插件为的还有第二个目的,利用其来管理用户登陆状态,详细看下面我为何要使用这个状态
在监听到某个公共页面访问的时候,_rbacPathCheck
函数将会:
rabcUpdateSuperAdminStatus
方法,在这里是页面实例的this.$vp.rabcUpdateSuperAdminStatus
方法(vue-viewplus将每一个模块导出的api绑定到页面实例即vm的$vp属性之下):// 登陆页面提交按钮绑定方法
submit() {
this.$refs.loginForm.validate(valid => {
if (valid) {
// 登陆
this.login({
vm: this,
username: this.formLogin.username,
password: this.formLogin.password,
imageCode: this.formLogin.code
}).then((res) => {
// 修改用户登陆状态
this.$vp.modifyLoginState(true);
// 解析服务端返回的登陆用户数据,获得菜单、权限相关数据
const isSuperAdminStatus = parseUserRoleIsSuperAdminStatus(res.principal.admin.roles);
this.$vp.toast('登陆成功', {
type: 'success'
});
// 重定向对象不存在则返回顶层路径
this.$router.replace(this.$route.query.redirect || '/')
})
} else {
// 登陆表单校验失败
this.$message.error('表单校验失败')
}
})
}
复制代码
若是不是则检测待访问的页面的path是否在**应用的公共页面paths列表_publicPaths
**中,若是是则放行
而作这个判断的前提是应用登陆成功以后须要将其得到受权的前端paths设置this.$vp.rabcUpdateAuthorizedPaths
给插件:
submit() {
this.$refs.loginForm.validate(valid => {
if (valid) {
// 登陆
this.login({
vm: this,
username: this.formLogin.username,
password: this.formLogin.password,
imageCode: this.formLogin.code
}).then((res) => {
this.$vp.rabcUpdateAuthorizedPaths(authorizeResources.paths);
})
} else {
// 登陆表单校验失败
this.$message.error('表单校验失败')
}
})
}
复制代码
数据的格式以下:
["/mngauth/admin", "/index", "/mngauth"]
复制代码
而且,数组的值支持为正则表达式;
若是不是则检查待访问页面的path是否在**登陆用户拥有访问权限的路由path路径集合_authorizedPaths
**中,若是是则放行,若是不是则整个校验结束,判断用户无权访问该页面,调用_onPathCheckFail
回调函数,通知应用,这里应用则会打印dialog提示用户
由于咱们的目的是抽象整个业务,因此这里才以回调的方式让应用有实际去感知和处理这一状况;
这样咱们就完成了第一个目标;
v-access
指令,对比声明的接口或资源别是否已经受权】这个目标,咱们的方案是:得到登陆用户的:
被受权角色所拥有的资源列表,对应的资源别名
数据格式相似:
["MNG_USERMNG", "MNG_ROLEMNG"]
复制代码
被受权角色所拥有的资源列表(或资源)所对应的后端接口集合
数据格式相似:
["admin/dels/*", "admin/search/*/*/*", "admin/*/*/*", "role/list/*", "admin/*"]
复制代码
可是默认但愿的是RESTful格式:
[{url: "admin/dels/*", method: "DELETE"}, ....]
复制代码
固然一样支持js正则表达式;
经过以上两组(二选一)受权数据,咱们就能够对比用户在指令中声明的条件权限进行对比。
定义一个Vue指令,这里命名为access
,其须要具有如下特色:
固然要理解上面的数据结构后端是怎么构建的,能够参考表结构和权限说明
咱们继续往上面的代码中添加逻辑,下面是代码实现:
const rbacModel = {
//....
/** * 更新受权接口集合 * @param interfaces */
rabcUpdateAuthorizeInterfaces(interfaces) {
_authorizeInterfaces = [...new Set(interfaces)]
this.cacheSaveToSessionStore('AUTHORIZED_INTERFACES', _authorizeInterfaces)
},
/** * 添加受权接口集合 * @param interfaces */
rabcAddAuthorizeInterfaces(interfaces) {
this::rbacModel.rabcUpdateAuthorizeInterfaces(_.concat(_authorizeInterfaces, interfaces))
},
/** * 更新资源别名集合 * @param alias */
rabcUpdateAuthorizeResourceAlias(alias) {
_authorizeResourceAlias = [...new Set(alias)]
this.cacheSaveToSessionStore('AUTHORIZED_RESOURCE_ALIAS', _authorizeResourceAlias)
},
/** * 添加资源别名集合 * @param alias */
rabcAddAuthorizeResourceAlias(alias) {
this::rbacModel.rabcUpdateAuthorizeResourceAlias(_.concat(_authorizeResourceAlias, alias))
},
install(Vue, {
//....
/** * [可选] 登陆用户拥有访问权限的资源别名集合 * {Array<Object>} * <p> * 数组中的item,能够是一个**正则表达式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也能够是一个字符串 * <p> * 匹配规则:由于若是都用`LoginStateCheck#authorizeInterfaces`接口进行匹配,可能有一种状况,访问一个资源,其须要n个接口,那么咱们在配置配置权限指令:v-access="[n, n....]"的时候就须要声明全部须要的接口,就会须要对比屡次, * 当咱们系统的接口集合很大的时候,势必会成为一个瓶颈,故咱们能够为资源声明一个别名,这个别名则能够表明这n个接口,这样的话就从n+减小到n次匹配; */
authorizeResourceAlias = [],
/** * [*] 登陆用户拥有访问权限的后台接口集合 * {Array<Object>} * <p> * 1.在`v-access`指令配置为url(默认)校验格式时,将会使用该集合和指令声明的待审查受权接口列表进行匹配,若是匹配成功,则指令校验经过,不然校验不经过,会将对应dom元素进行处理 * 2.TODO 将会用于在发送ajax请求以前,对待请求的接口和当前集合进行匹配,若是匹配失败说明用户就没有请求权限,则直接不发送后台请求,减小后端没必要要的资源浪费 * <p> * 数组中的item,能够是一个**正则表达式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也能够是一个字符串 * <p> * 匹配规则:将会用于在发送ajax请求以前,对待请求的接口和当前集合进行匹配,若是匹配失败说明用户就没有请求权限,则直接不发送后台请求,减小后端没必要要的资源浪费 * <p> * 注意须要根据`isRESTfulInterfaces`属性的值,来判断当前集合的数据类型: * * 若是`isRESTfulInterfaces`设置为`false`,则使用下面的格式: * ```json * ["admin/dels/*", ...] * ``` * 若是`isRESTfulInterfaces`设置为`true`,**注意这是默认设置**,则使用下面的格式: * ```json * [[{url: "admin/dels/*", method: "DELETE"}, ...]] * ``` */
authorizeInterfaces = [],
/** * [*] 声明`authorizeInterfaces`集合存储的是RESTful类型的接口仍是常规接口 * 1. 若是是(true),则`authorizeInterfaces`集合须要存储的结构就是: * [{url: 'admin/dels/*', method: 'DELETE'}] * 即进行接口匹配的时候会校验类型 * 2. 若是不是(false),则`authorizeInterfaces`集合须要存储的结构就是,即不区分接口类型: * ['admin/dels/*'] */
isRESTfulInterfaces = true
} = {}) {
//....
this::_createRBACDirective(Vue)
}
};
export default rbacModel;
复制代码
首先咱们在插件中添加几个字段和对应的设置接口:
isRESTfulInterfaces
authorizeInterfaces
authorizeResourceAlias
这样咱们就能够维护用户拥有的受权资源别名列表、资源(对应接口)后端接口数据列表,并默认认为接口为RESTful数据结构;
接着咱们就能够定义指令(在插件初始化方法install中),并在指令的bind
声明周期,解析对应UI组件声明的所需权限信息,并和持有的资源列表进行对比,若是对比失败则对UI组件作相应的显示或者disable
操做:
/** * 推荐使用资源标识配置:`v-access:alias[.disable]="'LOGIN'"` 前提须要注入身份认证用户所拥有的**受权资源标识集合**,由于这种方式能够较少比较的次数 * 传统使用接口配置:`v-access:[url][.disable]="'admin'"` 前提须要注入身份认证用户所拥有的**受权接口集合** * 两种都支持数组配置 * v-access:alias[.disable]="['LOGIN', 'WELCOME']" * v-access:[url][.disable]="['admin', 'admin/*']" * 针对于RESTful类型接口: * v-access="[{url: 'admin/search/*', method: 'POST'}]" * 默认使用url模式,由于这种方式比较通用 * v-access="['admin', 'admin/*']" * <p> * 其中`[.disbale]`用来标明在检测用户不具备对当前声明的权限时,将会把当前声明指令的`el`元素添加`el.disabled = true`,默认则是影藏元素:`el.style.display = 'none'` * <p> * 举例:`<el-form v-access="['admin/search']" slot="search-inner-box" :inline="true" :model="searchForm" :rules="searchRules" ref="ruleSearchForm" class="demo-form-inline">...</el-form>` * 上面这个检索表单须要登陆用户具备访问`'admin/search'`接口的权限,才会显示 * @param Vue * @private */
const _createRBACDirective = function(Vue) {
Vue.directive('access', {
bind: function(el, { value, arg, modifiers }) {
if (_superAdminStatus) {
return;
}
let isAllow = false
const statementAuth = _parseAccessDirectiveValue2Arr(value)
switch (arg) {
case 'alias':
isAllow = _checkPermission(statementAuth, _authorizeResourceAlias)
break
// 默认使用url模式
case 'url':
default:
if (_isRESTfulInterfaces) {
isAllow = _checkPermissionRESTful(statementAuth, _authorizeInterfaces)
} else {
isAllow = _checkPermission(statementAuth, _authorizeInterfaces)
}
}
if (!isAllow) {
if (_debug) {
console.warn(`[v+] RBAC access权限检测不经过:用户无权访问【${_.isObject(value) ? JSON.stringify(value) : value}】`);
}
if (_.has(modifiers, 'disable')) {
el.disabled = true;
el.style.opacity = '0.5'
} else {
el.style.display = 'none';
}
}
}
})
}
/** * 校验给定指令显示声明所需列表是否包含于身份认证用户所具备的权限集合中,若是是则返回`true`标识权限校验经过 * @param statementAuth * @param authorizeCollection * @returns {boolean} * @private */
const _checkPermission = function(statementAuth, authorizeCollection) {
let voter = []
statementAuth.forEach(url => {
voter.push(authorizeCollection.includes(url))
})
return !voter.includes(false)
}
/** * {@link _checkPermission} 附加了对接口类型的校验 * @param statementAuth * @param authorizeCollection * @returns {boolean} * @private */
const _checkPermissionRESTful = function(statementAuth, authorizeCollection) {
let voter = []
const expectedSize = statementAuth.length
const size = authorizeCollection.length
for (let i = 0; i < size; i++) {
const itf = authorizeCollection[i]
if (_.find(statementAuth, itf)) {
voter.push(true)
// 移除判断成功的声明权限对象
statementAuth.splice(i, 1)
}
}
// 若是投票获得的true含量和须要判断的声明权限长度一致,则标识校验经过
return voter.length === expectedSize
}
const _parseAccessDirectiveValue2Arr = function(value) {
let params = []
if (_.isString(value) || _.isPlainObject(value)) {
params.push(value)
} else if (_.isArray(value)) {
params = value
} else {
throw new Error('access 配置的受权标识符不正确,请检查')
}
return params
}
复制代码
在使用指令以前,咱们还须要解决插件所需权限列表的设置:
submit() {
this.$refs.loginForm.validate(valid => {
if (valid) {
// 登陆
this.login({
vm: this,
username: this.formLogin.username,
password: this.formLogin.password,
imageCode: this.formLogin.code
}).then((res) => {
// 修改用户登陆状态
this.$vp.modifyLoginState(true);
//...
const authorizeResources = parseAuthorizePaths(res.principal.admin.authorizeResources);
this.$vp.rabcUpdateAuthorizeResourceAlias(authorizeResources.alias);
const authorizeInterfaces = parseAuthorizeInterfaces(res.principal.admin.authorizeInterfaces);
this.$vp.rabcUpdateAuthorizeInterfaces(authorizeInterfaces);
//...
}
})
}
复制代码
这里的parseAuthorizePaths
和parseAuthorizeInterfaces
的做用是解析后端返回的登陆用户资源和接口列表,这个因人而异,就不贴了;
还须要注意的一点就是,this.$vp.modifyLoginState(true)
,是vue-viewplus插件登陆身份控制模块所提供的一个接口,其能够为应用维护登陆状态,好比在监控到后端返回会话超时时候自动将状态设置为false
,更多请查看*这里*,这也是逻辑复用的一个好处了;
固然若是你只是想实现本身的权限控制模块,并不想抽象的这么简单,也能够硬编码到项目中;
这样咱们就完成了第二个目标;
哦哦哦忘了写一下,咱们怎么用这个指令了,补充一下:
<el-form v-access="{url: 'admin/search/*/*/*', method: 'POST'}" slot="search-inner-box" :inline="true" :model="searchForm" :rules="searchRules" ref="ruleSearchForm" class="demo-form-inline">
//...
</el-form>
复制代码
上面是一个最简单的例子,即声明,若是要使用该检索功能,须要用户拥有:{url: 'admin/search/*/*/*', method: 'POST'
这个接口权限;
另外指令的更多声明方式,请查看这里
得到登陆用户的:
this.$vp.rabcUpdateAuthorizeInterfaces(authorizeInterfaces);
,这里只要复用便可拦截请求,这里咱们应用请求都是基于vue-viewplus的util-http.js 针对axios进行了二次封装的ajax模块来发送,它的好处是我80%的请求接口不用单独写错误处理代码,而是由改模块自动处理了,回到正题,咱们怎么拦截请求,由于该ajax插件底层使用的是axios,对应的其提供了咱们拦截请求的钩子https://github.com/Jiiiiiin/jiiiiiin-security#表结构和权限说明)
在具有以上条件以后咱们好像就能够写代码了,嘿嘿:)
咱们继续往上面的代码中添加逻辑,下面是代码实现:
const rbacModel = {
//...
install(Vue, {
//...
/** * [*] `$vp::onPathCheckFail(to, from, next)` * <p> * 发送ajax请求时权限检查失败时被回调 */
onAjaxReqCheckFail = null
} = {}) {
_onAjaxReqCheckFail = onAjaxReqCheckFail;
this::_rbacAjaxCheck()
}
};
复制代码
仍是在插件对象中,首先声明了所需配置的onAjaxReqCheckFail
,其次调用_rbacAjaxCheck
进行axios拦截声明:
/** * 用于在发送ajax请求以前,对待请求的接口和当前集合进行匹配,若是匹配失败说明用户就没有请求权限,则直接不发送后台请求,减小后端没必要要的资源浪费 * @private */
const _rbacAjaxCheck = function() {
this.getAjaxInstance().interceptors.request.use(
(config) => {
const { url, method } = config
const statementAuth = []
let isAllow
if (_isRESTfulInterfaces) {
const _method = _.toUpper(method)
statementAuth.push({ url, method: _method });
isAllow = _checkPermissionRESTful(statementAuth, _authorizeInterfaces)
// TODO 由于拦截到的请求`{url: "admin/0/1/10", method: "GET"}` 没有找到相似java中org.springframework.util.AntPathMatcher;
// 那样能匹配`{url: "admin/*/*/*", method: "GET"}`,的方法`temp = antPathMatcher.match(anInterface.getUrl(), reqURI)`
// 故这个需求暂时无法实现 :)
console.log('statementAuth', isAllow, statementAuth, _authorizeInterfaces)
} else {
isAllow = _checkPermission(statementAuth, _authorizeInterfaces)
}
if (isAllow) {
return config;
} else {
if (_debug) {
console.warn(`[v+] RBAC ajax权限检测不经过:用户无权发送请求【${method}-${url}】`);
}
if (_.isFunction(_onAjaxReqCheckFail)) {
this::_onAjaxReqCheckFail(config);
} else {
throw new Error('check_authorize_ajax_req_fail');
}
}
},
error => {
return Promise.reject(error)
}
)
}
复制代码
这里可能this.getAjaxInstance()
不知道是什么,在调用_rbacAjaxCheck
是咱们指定了this,即this::_rbacAjaxCheck()
,而这个this
就是$vp
对象,即vue-viewplus绑定到Vue实例的$vp
属性;
其余的就很简单了,根据配置的_isRESTfulInterfaces
属性看咱们要校验的是RESTful接口仍是普通接口,若是校验经过则返回axios所需请求config,若是失败则调用配置的_onAjaxReqCheckFail
通知应用,让应用去处理权限失败的状况,通常也是弹出一个toast
提示用户权限不足。
这样好像咱们就完成了全部目标,哈哈哈。
写文章真是比敲代码累得多呀。
可是不幸的是咱们并无实现第三个目标,问题就在于,上面代码片断的TODO中所描述,我没有解决RESTful PathValue类型接口的权限对比,后端我用的库是经过:
log.debug("内管权限校验开始:{} {} {}", admin.getUsername(), reqURI, reqMethod);
for (Role role : roles) {
boolean temp;
for (Resource resource : role.getResources()) {
for (Interface anInterface : resource.getInterfaces()) {
temp = antPathMatcher.match(anInterface.getUrl(), reqURI) && reqMethod.equalsIgnoreCase(anInterface.getMethod());
if (temp) {
hasPermission = true;
break;
}
}
}
}
复制代码
org.springframework.util.AntPathMatcher
提供的方法来完成的,可是js我没有找到合适的库来对比:
{url: "admin/*/*/*", method: "GET"} <> {url: "admin/0/1/10", method: "GET"}
复制代码
这样的两个对象,因此有耐心看到这里的朋友,若是你解决了这个问题,请联系我,谢谢。
谢谢你耐心的看到这里,若是以为对你有所帮助,请帮忙支持一下个人两个项目:
动动小手,求star,哎,哈哈哈。