研发图纸文件管理系统-前端项目
iczer
2020-08-02 9b96868586541a305b5e3cb95288c6238679d51f
feat: add authorize directive; :star2:
新增:权限验证指令;
1个文件已添加
6个文件已修改
203 ■■■■■ 已修改文件
src/mock/user/login.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/plugins/authority-plugin.js 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/plugins/index.js 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/account.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/theme/default/style.less 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/routerUtil.js 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mock/user/login.js
@@ -22,8 +22,8 @@
    result.data.user = user
    result.data.token = 'Authorization:' + Math.random()
    result.data.expireAt = new Date(new Date().getTime() + 30 * 60 * 1000)
    result.data.permissions = [{id: 'analysis', extra: ['add', 'edit', 'delete']}]
    result.data.roles = [{id: 'admin', extra: ['add', 'edit', 'delete']}]
    result.data.permissions = [{id: 'queryForm', operation: ['add', 'edit', 'delete']}]
    result.data.roles = [{id: 'admin', operation: ['add', 'edit', 'delete']}]
  }
  return result
})
src/plugins/authority-plugin.js
New file
@@ -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
src/plugins/index.js
@@ -1,7 +1,10 @@
import VueI18nPlugin from '@/plugins/i18n-extend';
import VueI18nPlugin from '@/plugins/i18n-extend'
import AuthorityPlugin from '@/plugins/authority-plugin'
const Plugins = {
  install: function (Vue) {
    Vue.use(VueI18nPlugin)
    Vue.use(AuthorityPlugin)
  }
}
export default Plugins
src/router/index.js
@@ -1,11 +1,12 @@
import Vue from 'vue'
import Router from 'vue-router'
import {formatAuthority} from '@/utils/routerUtil'
Vue.use(Router)
// 不需要登录拦截的路由配置
const loginIgnore = {
  names: ['404'],      //根据路由名称匹配
  names: ['404', '403'],      //根据路由名称匹配
  paths: ['/login'],   //根据路由fullPath匹配
  /**
   * 判断路由是否包含在该配置中
@@ -24,6 +25,7 @@
 */
function initRouter(isAsync) {
  const options = isAsync ? require('./config.async').default : require('./config').default
  formatAuthority(options.routes)
  return new Router(options)
}
export {loginIgnore, initRouter}
src/store/modules/account.js
@@ -2,9 +2,9 @@
  namespaced: true,
  state: {
    user: undefined,
    permissions: [],
    roles: [],
    routesConfig: []
    permissions: null,
    roles: null,
    routesConfig: null
  },
  getters: {
    user: state => {
@@ -19,10 +19,11 @@
      return state.user
    },
    permissions: state => {
      if (!state.permissions || state.permissions.length === 0) {
      if (!state.permissions) {
        try {
          const permissions = localStorage.getItem(process.env.VUE_APP_PERMISSIONS_KEY)
          state.permissions = JSON.parse(permissions)
          state.permissions = state.permissions ? state.permissions : []
        } catch (e) {
          console.error(e.message)
        }
@@ -30,10 +31,11 @@
      return state.permissions
    },
    roles: state => {
      if (!state.roles || state.roles.length === 0) {
      if (!state.roles) {
        try {
          const roles = localStorage.getItem(process.env.VUE_APP_ROLES_KEY)
          state.roles = JSON.parse(roles)
          state.roles = state.roles ? state.roles : []
        } catch (e) {
          console.error(e.message)
        }
@@ -41,10 +43,11 @@
      return state.roles
    },
    routesConfig: state => {
      if (!state.routesConfig || state.routesConfig.length === 0) {
      if (!state.routesConfig) {
        try {
          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) {
          console.error(e.message)
        }
src/theme/default/style.less
@@ -26,3 +26,7 @@
    border-right: 1px solid rgba(98, 98, 98, 0.2);
  }
}
.disabled{
  cursor: not-allowed;
  opacity: 0.4;
}
src/utils/routerUtil.js
@@ -64,6 +64,7 @@
  if (asyncRoutes) {
    if (routesConfig && routesConfig.length > 0) {
      const routes = parseRoutes(routesConfig, routerMap)
      formatAuthority(routes)
      const finalRoutes = mergeRoutes(router.options.routes, routes)
      router.options = {...router.options, routes: finalRoutes}
      router.matcher = new Router({...router.options, routes:[]}).matcher
@@ -151,7 +152,37 @@
  if (typeof authority === 'object') {
    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}