feat: add authorize directive; 🌟

新增:权限验证指令;
master
iczer 5 years ago
parent 55358b4107
commit 9b96868586
  1. 4
      src/mock/user/login.js
  2. 134
      src/plugins/authority-plugin.js
  3. 5
      src/plugins/index.js
  4. 4
      src/router/index.js
  5. 17
      src/store/modules/account.js
  6. 4
      src/theme/default/style.less
  7. 35
      src/utils/routerUtil.js

@ -22,8 +22,8 @@ Mock.mock('/login', 'post', ({body}) => {
result.data.user = user result.data.user = user
result.data.token = 'Authorization:' + Math.random() result.data.token = 'Authorization:' + Math.random()
result.data.expireAt = new Date(new Date().getTime() + 30 * 60 * 1000) result.data.expireAt = new Date(new Date().getTime() + 30 * 60 * 1000)
result.data.permissions = [{id: 'analysis', extra: ['add', 'edit', 'delete']}] result.data.permissions = [{id: 'queryForm', operation: ['add', 'edit', 'delete']}]
result.data.roles = [{id: 'admin', extra: ['add', 'edit', 'delete']}] result.data.roles = [{id: 'admin', operation: ['add', 'edit', 'delete']}]
} }
return result return result
}) })

@ -0,0 +1,134 @@
/**
* 获取路由需要的权限
* @param permissions
* @param route
* @returns {*}
*/
const getRoutePermission = (permissions, route) => permissions.find(item => item.id === route.meta.authority.permission)
/**
* 获取路由需要的角色
* @param roles
* @param route
* @returns {*}
*/
const getRouteRole = (roles, route) => roles.find(item => item.id === route.meta.authority.role)
/**
* 判断是否已为方法注入权限认证
* @param method
* @returns {boolean}
*/
const hasInjected = (method) => method.toString().indexOf('//--auth-inject') !== -1
/**
* 操作权限校验
* @param authConfig
* @param permission
* @param role
* @param permissions
* @param roles
* @returns {boolean}
*/
const auth = function(authConfig, permission, role, permissions, roles) {
const {check, type} = authConfig
if (check && typeof check === 'function') {
return check(permission, role, permissions, roles)
} else {
if (type === 'permission') {
return permission && permission.operation && permission.operation.indexOf(check) !== -1
} else if (type === 'role') {
return role && role.operation && role.operation.indexOf(check) !== -1
}
}
return false
}
/**
* 阻止的 click 事件监听
* @param event
* @returns {boolean}
*/
const preventClick = function (event) {
event.stopPropagation()
return false
}
const checkInject = function (el, binding,vnode) {
const type = binding.arg
const check = binding.value
const instance = vnode.componentInstance
const $auth = instance.$auth
if (!$auth || !$auth(check, type)) {
el.classList.add('disabled')
el.setAttribute('title', '无此权限')
el.addEventListener('click', preventClick, true)
} else {
el.classList.remove('disabled')
el.removeAttribute('title')
el.removeEventListener('click', preventClick, true)
}
}
const AuthorityPlugin = {
install(Vue) {
Vue.directive('auth', {
bind(el, binding,vnode) {
checkInject(el, binding, vnode)
},
update(el, binding,vnode) {
checkInject(el, binding, vnode)
}
})
Vue.mixin({
beforeCreate() {
if (this.$options.authorize) {
const authorize = this.$options.authorize
Object.keys(authorize).forEach(key => {
if (this.$options.methods[key]) {
const method = this.$options.methods[key]
if (!hasInjected(method)) {
let authConfig = authorize[key]
authConfig = (typeof authConfig === 'string') ? {check: authConfig} : authConfig
const {check, type, onFailure} = authConfig
this.$options.methods[key] = function () {
//--auth-inject
if (this.$auth(check, type)) {
return method(...arguments)
} else {
if (onFailure && typeof onFailure === 'function') {
this[`$${check}Failure`] = onFailure
return this[`$${check}Failure`](check)
} else {
this.$message.error(`对不起,您没有操作权限:${check}`)
}
return 0
}
}
}
}
})
}
},
methods: {
/**
* 操作权限校验
* @param check 需要校验的操作名
* @param type 校验类型通过 permission 校验还是通过 role 校验
* 如未设置则自动识别如匹配到当前路由 permission type = permission否则 type = role
* @returns {boolean} 是否校验通过
*/
$auth(check, type) {
const permissions = this.$store.getters['account/permissions']
const roles = this.$store.getters['account/roles']
const permission = getRoutePermission(permissions, this.$route)
const role = getRouteRole(roles, this.$route)
if (!type) {
type = permission ? 'permission' : 'role'
}
return auth({check, type}, permission, role, permissions, roles)
}
}
})
}
}
export default AuthorityPlugin

@ -1,7 +1,10 @@
import VueI18nPlugin from '@/plugins/i18n-extend'; import VueI18nPlugin from '@/plugins/i18n-extend'
import AuthorityPlugin from '@/plugins/authority-plugin'
const Plugins = { const Plugins = {
install: function (Vue) { install: function (Vue) {
Vue.use(VueI18nPlugin) Vue.use(VueI18nPlugin)
Vue.use(AuthorityPlugin)
} }
} }
export default Plugins export default Plugins

@ -1,11 +1,12 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
import {formatAuthority} from '@/utils/routerUtil'
Vue.use(Router) Vue.use(Router)
// 不需要登录拦截的路由配置 // 不需要登录拦截的路由配置
const loginIgnore = { const loginIgnore = {
names: ['404'], //根据路由名称匹配 names: ['404', '403'], //根据路由名称匹配
paths: ['/login'], //根据路由fullPath匹配 paths: ['/login'], //根据路由fullPath匹配
/** /**
* 判断路由是否包含在该配置中 * 判断路由是否包含在该配置中
@ -24,6 +25,7 @@ const loginIgnore = {
*/ */
function initRouter(isAsync) { function initRouter(isAsync) {
const options = isAsync ? require('./config.async').default : require('./config').default const options = isAsync ? require('./config.async').default : require('./config').default
formatAuthority(options.routes)
return new Router(options) return new Router(options)
} }
export {loginIgnore, initRouter} export {loginIgnore, initRouter}

@ -2,9 +2,9 @@ export default {
namespaced: true, namespaced: true,
state: { state: {
user: undefined, user: undefined,
permissions: [], permissions: null,
roles: [], roles: null,
routesConfig: [] routesConfig: null
}, },
getters: { getters: {
user: state => { user: state => {
@ -19,10 +19,11 @@ export default {
return state.user return state.user
}, },
permissions: state => { permissions: state => {
if (!state.permissions || state.permissions.length === 0) { if (!state.permissions) {
try { try {
const permissions = localStorage.getItem(process.env.VUE_APP_PERMISSIONS_KEY) const permissions = localStorage.getItem(process.env.VUE_APP_PERMISSIONS_KEY)
state.permissions = JSON.parse(permissions) state.permissions = JSON.parse(permissions)
state.permissions = state.permissions ? state.permissions : []
} catch (e) { } catch (e) {
console.error(e.message) console.error(e.message)
} }
@ -30,10 +31,11 @@ export default {
return state.permissions return state.permissions
}, },
roles: state => { roles: state => {
if (!state.roles || state.roles.length === 0) { if (!state.roles) {
try { try {
const roles = localStorage.getItem(process.env.VUE_APP_ROLES_KEY) const roles = localStorage.getItem(process.env.VUE_APP_ROLES_KEY)
state.roles = JSON.parse(roles) state.roles = JSON.parse(roles)
state.roles = state.roles ? state.roles : []
} catch (e) { } catch (e) {
console.error(e.message) console.error(e.message)
} }
@ -41,10 +43,11 @@ export default {
return state.roles return state.roles
}, },
routesConfig: state => { routesConfig: state => {
if (!state.routesConfig || state.routesConfig.length === 0) { if (!state.routesConfig) {
try { try {
const routesConfig = localStorage.getItem(process.env.VUE_APP_ROUTES_KEY) const routesConfig = localStorage.getItem(process.env.VUE_APP_ROUTES_KEY)
state.routesConfig = eval(routesConfig) ? JSON.parse(routesConfig) : state.routesConfig state.routesConfig = JSON.parse(routesConfig)
state.routesConfig = state.routesConfig ? state.routesConfig : []
} catch (e) { } catch (e) {
console.error(e.message) console.error(e.message)
} }

@ -26,3 +26,7 @@
border-right: 1px solid rgba(98, 98, 98, 0.2); border-right: 1px solid rgba(98, 98, 98, 0.2);
} }
} }
.disabled{
cursor: not-allowed;
opacity: 0.4;
}

@ -64,6 +64,7 @@ function loadRoutes({router, store, i18n}, routesConfig) {
if (asyncRoutes) { if (asyncRoutes) {
if (routesConfig && routesConfig.length > 0) { if (routesConfig && routesConfig.length > 0) {
const routes = parseRoutes(routesConfig, routerMap) const routes = parseRoutes(routesConfig, routerMap)
formatAuthority(routes)
const finalRoutes = mergeRoutes(router.options.routes, routes) const finalRoutes = mergeRoutes(router.options.routes, routes)
router.options = {...router.options, routes: finalRoutes} router.options = {...router.options, routes: finalRoutes}
router.matcher = new Router({...router.options, routes:[]}).matcher router.matcher = new Router({...router.options, routes:[]}).matcher
@ -151,7 +152,37 @@ function hasRole(route, roles) {
if (typeof authority === 'object') { if (typeof authority === 'object') {
required = authority.role required = authority.role
} }
return authority === '*' || (required && roles.findIndex(item => item === required || item.id === required) !== -1) return authority === '*' || (required && roles && roles.findIndex(item => item === required || item.id === required) !== -1)
} }
export {parseRoutes, loadRoutes, loginGuard, authorityGuard} /**
* 格式化路由的权限配置
* @param routes
*/
function formatAuthority(routes) {
routes.forEach(route => {
const meta = route.meta
if (meta) {
let authority = {}
if (!meta.authority) {
authority.permission = '*'
}else if (typeof meta.authority === 'string') {
authority.permission = meta.authority
} else if (typeof meta.authority === 'object') {
authority = meta.authority
} else {
console.log(typeof meta.authority)
}
meta.authority = authority
} else {
route.meta = {
authority: {permission: '*'}
}
}
if (route.children) {
formatAuthority(route.children)
}
})
}
export {parseRoutes, loadRoutes, loginGuard, authorityGuard, formatAuthority}

Loading…
Cancel
Save