vue-element-admin 登陆及目录权限控制的实现

作者:乐之终曲 时间:2024-04-28 09:26:29 

登陆

万事开头难,做什么事都要有个起点,后面才能更好的进行下去,因此我选择的起点就是最为直观的登陆页面 /login/index.vue

/src/views/login/index

去除那些无关的东西,比如什么 rules 校验啊,默认的账号密码之类的东西,直接看核心登陆方法 handleLogin

handleLogin() {
     this.$refs.loginForm.validate(valid => {
       if (valid) {
         this.loading = true
         # 请求 store 中的方法
         this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
           this.loading = false
           this.$router.push({ path: this.redirect || '/' })
         }).catch((errorMsg) => {
           this.loading = false
           this.$message({
             type: 'error',
             message: errorMsg
           })
         })
       } else {
         console.log('error submit!!')
         return false
       }
     })
   },

store 主要事做一些缓存之类的处理,这里调用了 store 中的 LoginByUsername 把表单内容传过去,这里我就改了点 catch 把我自己需要的错误信息进行提示,其他东西不需要改动

/src/store/modules/user

上面登陆中请求的 LoginByUsername 正是在这

// 用户名登录
   LoginByUsername({ commit }, userInfo) {
     const username = userInfo.username.trim()
     return new Promise((resolve, reject) => {
       // 请求后台登陆
       loginByUsername(username, userInfo.password).then(response => {
         const data = response.data
         if (response.data.errorCode !== 200) {
           // 登陆失败,回传提示信息
           reject(data.errorMsg)
         }
         // 设置 token,作为用户已登陆的前端标识,存在 cookie 中
         commit('SET_TOKEN', data.retData)
         setToken(data.retData)
         resolve()
       }).catch(error => {
         reject(error)
       })
     })
   },

setToken() 方法会把 token 保存到 cookie 里,很重要

下面有个 GetUserInfo 方法,在你登陆的时候会去获取你的权限数据

// 获取用户信息
   GetUserInfo({ commit, state }) {
     return new Promise((resolve, reject) => {
       // 请求获取权限
       getUserInfo(state.token).then(response => {
         if (response.status !== 200) { // 由于mockjs 不支持自定义状态码只能这样hack
           reject('error')
         }
         const data = response.data

if (data.retData.module && data.retData.module.length > 0) { // 验证返回的roles是否是一个非空数组
           commit('SET_ROLES', data.retData.module)
         } else {
           // 当用户无任何权限时设置
           commit('SET_ROLES', ['普通用户'])
         }

commit('SET_NAME', data.retData.username)
         commit('SET_AVATAR', 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif')
         commit('SET_INTRODUCTION', data.retData.username)
         resolve(response)
       }).catch(error => {
         reject(error)
       })
     })
   },

不需要知道那个 new Promise 啥的干啥用,反正我不知道,只要知道 getUserInfo 这个方法就行了,这个方法会以上面之前保存的 token 为参数去请求获取你的用户权限,原逻辑是没有权限就跳登陆页面,我这系统需要,没权限也有个首页可看,所以
SET_ROLES 参数给了个“普通用户”,反正什么值无所谓有值,没权限就行。
下面 commit 里的参数,分别为,用户名,头像,介绍,保留就好,也可自定义。
PS:在每次页面跳转时候页面都会走一个 GetUserInfo ,以此来判断用户有没有失效,因此,我的做法是直接把用户信息包括权限保存在 redis 里取,不要每次都从库里查

/src/utils/request.js

此文件是请求与返回 * ,我们看返回值 *

每次你请求从后台接口返回时,都会经过返回值 * ,默认的逻辑是返回的 code 不为 20000 时候就会被拦截,这里需要我们改为自己的逻辑,很多人配置都对,点登录一直 error 就是因为这个原因

// response interceptor
service.interceptors.response.use(
 response => response,
 /**
  * 下面的注释为通过在response里,自定义code来标示请求状态
  * 当code返回如下情况则说明权限有问题,登出并返回到登录页
  * 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
  * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
  */
 // response => {
 //   const res = response.data
 //   if (res.code !== 20000) {
 //     Message({
 //       message: res.message,
 //       type: 'error',
 //       duration: 5 * 1000
 //     })
 //     // 50008:非法的token; 50012:其他客户端登录了;  50014:Token 过期了;
 //     if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
 //       // 请自行在引入 MessageBox
 //       // import { Message, MessageBox } from 'element-ui'
 //       MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
 //         confirmButtonText: '重新登录',
 //         cancelButtonText: '取消',
 //         type: 'warning'
 //       }).then(() => {
 //         store.dispatch('FedLogOut').then(() => {
 //           location.reload() // 为了重新实例化vue-router对象 避免bug
 //         })
 //       })
 //     }
 //     return Promise.reject('error')
 //   } else {
 //     return response.data
 //   }
 // },
 error => {
   console.log('err' + error) // for debug
   Message({
     message: '连接异常',
     // message: error.message,
     type: 'error',
     duration: 5 * 1000
   })
   return Promise.reject(error)
 }
)

/src/api/login

接口的请求都会从这里去请求后台,就不多赘述,基本只需要改动下请求路径就ok了。
登出 logout 也是,请求保证后台注销,前端处理部分也是在 store/modules/user 里的 LogOut 方法,基本不需要改动。

目录权限

大多数系统都有根据用户权限,或者说角色,展示相应页面的要求

/src/router/index

结合框架来实现目录的控制,如果是传统项目,一般是把目录结构整个存在后台数据库里,然后前端循环展示出来,但是这个在这里不需要这么麻烦,只需要把每个页面对应的权限保存起来就行了

目录在分为两个部分 constantRouterMap 与 asyncRouterMap
constantRouterMap:主要是通用部分,每个用户都有的页面
asyncRouterMap:需要进行权限过滤的页面

所以像首页这种都丢在 constantRouterMap 里,而像权限管理页面这种放在 asyncRouterMap 里
在目录上加上 permission 标识,这个是我自定义的唯一标识,用于区分每个页面的权限,我这里直接以页面的路径为值,因为路径肯定唯一

{
   path: '',
   component: Layout,
   alwaysShow: true,
   redirect: '/xxx/xxx',
   name: 'xxx',
   meta: {
     title: 'xxx',
     icon: 'xxx'
   },
   children: [
     {
       path: '/xxx/xxx',
       component: () => import('@/views/xxx/xxx'),
       name: 'xxx',
       meta: {
         title: 'xxx',
         icon: 'message',
         # 权限标识,每个目录唯一
         permission: '/xxx/xxx',
         noCache: false }
     }
   ]
 },

然后就只需要在加载目录时对目录进行判断就可以了

/src/permission

这个文件就是整个路由逻辑在这

vue-element-admin 登陆及目录权限控制的实现

从这里的逻辑可以看到,登陆后,现在判断了用户权限,如果没权限就会进入之前说到的 GetUserInfo 方法去获取权限,由于要对目录进行控制,所以在 GetUserInfo 里我们也需要获取到目录的权限列表,只需要获取到有的就行了,没有权限的目录就不需要获取。
在 GetUserInfo 的最后通过 resolve 方法把返回值返回这个页面,截图中  module 就是我获取到的有权限的目录列表,然后通过
GenerateRoutes 来生成要加载的目录,接下来就是改这了
PS:不少人加我 QQ,说不明白这里什么意思,以及不知道要怎样的目录数据,我做个简要的说明。从这个 permission 文件中我们可以看到目录渲染一共三步:
1. GetUserInfo 中获取到你的目录数据(我权限数据全放这个方法里获取了:用户数据,目录权限,数据权限等),但这里主要的就是目录权限
2.在上门这个 permission.js 目录渲染的逻辑中获取 GetUserInfo 中获取到的目录权限,也就是上面那句
const roles = res.data.retData.module 这个 module 就是我返回值中目录权限的部分,module 就是模块的意思
3.也就是下一步,在 GenerateRoutes 方法中把 roles 目录权限传进去进行目录渲染

然后目录权限的数据到底长什么样?先来看下表结构

vue-element-admin 登陆及目录权限控制的实现

我代码中拿到的数据:

vue-element-admin 登陆及目录权限控制的实现

我们要用的只有这个 permission 字段,说简单点,你既可以和我一样直接拿到目录对象的所有数据,也可以单单取这个 permission 字段组成的数组,看自己习惯,具体后面判断逻辑在下面目录渲染的部分,单单数组的逻辑会更加简单点

/src/store/modules/permission

找到 GenerateRoutes

const permission = {
 state: {
   routers: constantRouterMap,
   addRouters: []
 },
 mutations: {
   SET_ROUTERS: (state, routers) => {
     state.addRouters = routers
     state.routers = constantRouterMap.concat(routers)
   }
 },
 actions: {
   GenerateRoutes({ commit }, data) {
     return new Promise(resolve => {
       const { roles } = data
       // 权限对列表进行过滤
       const accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
       commit('SET_ROUTERS', accessedRouters)
       resolve()
     })
   }
 }
}

对于通用目录我们不需要管,在原逻辑中,这里通过判断如果用户是管理员就直接把整个权限目录都加载,如果不是再进行过滤,因为我们这完全就通过后台控制,包括管理员所以就把判断去掉了,直接对目录进行过滤

找到 filterAsyncRouter 方法

/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
* @param routes asyncRouterMap
* @param roles
*/
function filterAsyncRouter(routes, roles) {
 const res = []
 routes.forEach(route => {
   const tmp = { ...route }
   if (hasPermission(roles, tmp)) {
     // 是否存在子节点,存在子节点说明当前节点为展开栏,并非页面
     if (tmp.children) {
       tmp.children = filterAsyncRouter(tmp.children, roles)
       // forCheck() 方法递归判断该节点下是否存在页面,不存在则隐藏
       // true:不存在,false:存在
       tmp.hidden = forCheck(tmp.children)
     }
     res.push(tmp)
   }
 })

return res
}

/**
* 递归子节点,判断是否存在展示页面,存在返回 false,不存在返回 true
* @param routes
*/
function forCheck(routes) {
 // 设置默认为隐藏
 let isHidden = true
 // 判断是否存页面,不存在说明该节点下不存在页面
 if (routes != null && routes.length > 0) {
   // 循环子目录,如果子目录中不存在需要权限页面
   // 说明子页面全是展开栏,隐藏
   for (const route of routes) {
     // 存在 permission 说明为页面,不存在说明为展开栏,将子页面列表继续递归
     if (route.meta && route.meta.permission) {
       isHidden = false
       return
     } else {
       isHidden = forCheck(route.children)
     }
   }
 }
 return isHidden
}

通过循环权限目录通过 hasPermission 方法进行判断
forCheck 是我自己封装的方法,因为项目中不只存在二级目录,所以,通过递归的方式,判断子节点下是否有展示页,也就是带 permission 的页面,如果有,返回 false,没有返回 true

/**
* 通过meta.role判断是否与当前用户权限匹配
* @param roles
* @param route
*/
function hasPermission(roles, route) {
 if (route.meta && route.meta.permission) {
   return roles.some(role => route.meta.permission.includes(role.permission))
 } else {
   return true
 }
}

hasPermission 方法,这里,我们之前加的 permission 参数就起到作用了
如果有 permission 就进行判断, roles 参数就是 /src/permission 里传过来的 module 目录。
通过 roles.some 循环 roles 别名 role,如果目录里 includes(包含)这个目录权限,就返回true,否则 false,为 true 的会被显示,当然如果直接没有 permission 就说明不需要后台管控,就直接 true,这样目录就被加载出来了。

来源:https://linjinp.blog.csdn.net/article/details/90256713

标签:vue-element-admin,登陆,目录,权限
0
投稿

猜你喜欢

  • Python比较配置文件的方法实例详解

    2021-11-27 05:54:35
  • python3使用print打印带颜色的字符串代码实例

    2022-01-20 10:30:49
  • python 类的继承 实例方法.静态方法.类方法的代码解析

    2021-12-19 14:19:34
  • 几个SQL SERVER应用问题解答

    2008-01-01 19:12:00
  • 关于设计的六个误会

    2008-06-26 18:18:00
  • Jsp+Servlet实现文件上传下载 删除上传文件(三)

    2023-06-27 16:29:29
  • TensorFlow实现自定义Op方式

    2021-11-01 15:56:38
  • python中几个常用函数的正确用法-lambda/filter/map/reduce

    2022-02-17 14:52:45
  • Django实现自定义404,500页面教程

    2021-06-20 06:23:07
  • Go+Vue开发一个线上外卖应用的流程(用户名密码和图形验证码)

    2024-05-09 14:52:17
  • 在MySQL中使用XML数据—数据格式化

    2009-12-29 10:26:00
  • python如何实现从视频中提取每秒图片

    2023-10-15 11:36:25
  • ASP 三层架构 Convert类实现代码

    2011-03-16 11:01:00
  • 攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)

    2023-09-05 19:29:06
  • vue中的mescroll搜索运用及各种填坑处理

    2024-04-30 10:26:39
  • SQL Server 2005 还原数据库错误解决方法

    2024-01-14 05:45:43
  • 修改mysql密码与忘记mysql密码的处理方法

    2024-01-23 21:53:48
  • python中的Elasticsearch操作汇总

    2022-01-29 10:44:45
  • Python实现多线程下载文件的代码实例

    2022-11-06 02:41:12
  • python3批量删除豆瓣分组下的好友的实现代码

    2022-02-14 22:27:13
  • asp之家 网络编程 m.aspxhome.com