将权限管理应用到系统,首先做好登录, 点击登录按钮后,触发以下动作

  • vuex 中的 login 动作,设置 cookie
  • vuex 中的 getuserinfo , 获取权限、用户名、头像等

    由于目前未使用连接后端服务器,所以使用 mockjs 拦截请求并返回。

    github中查看

1 全局请求拦截

使用axios 封装好请求和响应

src/utils/request.js

  1. import axios from 'axios'
  2. const clearRequest = {
  3. source: {
  4. token: null,
  5. cancel: null
  6. }
  7. }
  8. const cancelToken = axios.CancelToken
  9. const source = cancelToken.source()
  10. // 创建 axios 实例
  11. const service = axios.create({
  12. cancelToken: source.token,
  13. timeout: 6000, // 请求超时时间
  14. })
  15. // request 拦截器
  16. service.interceptors.request.use(
  17. config => {
  18. config.cancelToken = clearRequest.source.token
  19. return config
  20. },
  21. error => {
  22. return Promise.reject(error)
  23. }
  24. )
  25. // response 拦截器
  26. service.interceptors.response.use(
  27. resp => resp,
  28. error => {
  29. return Promise.reject(error)
  30. }
  31. )
  32. export { clearRequest }
  33. export default service

2 封装一些常用的函数

src/utils/index.js

  1. export function param2Obj(url){
  2. const search = url.split('?')[1]
  3. if(!search){
  4. return {}
  5. }
  6. return JSON.parse(
  7. '{"' +
  8. decodeURIComponent(search)
  9. .replace(/"/g, '\\"')
  10. .replace(/&/g, '","')
  11. .replace(/=/g, '":"') +
  12. '"}'
  13. )
  14. }

src/utils/auth.js

  1. import Cookies from 'js-cookie'
  2. const tokenKey = 'X-Token'
  3. export function getToken(){
  4. return Cookies.get(tokenKey)
  5. }
  6. export function setToken(token){
  7. return Cookies.set(tokenKey, token)
  8. }
  9. export function removeToken(){
  10. return Cookies.remove(tokenKey)
  11. }

3 编写登录api

src/api/login.js 文件

  1. import request from '@/utils/request'
  2. export function loginByUsernameApi(username, password){
  3. return request({
  4. url: '/api/auth/api-token-auth',
  5. method: 'post',
  6. data: {
  7. username,
  8. password
  9. }
  10. })
  11. }
  12. export function getUserInfoApi(token){
  13. return request({
  14. url: '/api/userinfo',
  15. method: 'get',
  16. params: {token}
  17. })
  18. }
  19. export function logoutApi(){
  20. return request({
  21. url: '/api/auth/logout',
  22. method: 'post'
  23. })
  24. }

4 mock 拦截

src/mock/index.js

  1. import Mock from 'mockjs'
  2. import login from './login'
  3. // 登录相关
  4. Mock.mock(/\/api\/auth\/api-token-auth/, 'post', login.loginByUsername)
  5. Mock.mock(/\/api\/auth\/logout/, 'post', login.logout)
  6. Mock.mock(/\/api\/userinfo/, 'get', login.getUserInfo)
  7. export default Mock

src/mock/login.js

  1. import { param2Obj } from '@/utils'
  2. const usermap = {
  3. admin: {
  4. token: 'admin',
  5. introduction: '我是超级管理员',
  6. name: 'Super Admin',
  7. pass: 'e10adc3949ba59abbe56e057f20f883e',
  8. roles: ['admin']
  9. },
  10. developer: {
  11. token: 'developer',
  12. introduction: '我是开发',
  13. name: '工程师小王',
  14. pass: 'e10adc3949ba59abbe56e057f20f883e',
  15. roles: ['/system', '/system/permit', '/system/permit/account']
  16. }
  17. }
  18. export default {
  19. loginByUsername: config => {
  20. const { username, password } = JSON.parse(config.body)
  21. console.log('loginByUsername username, password: ', username, password)
  22. if(username === 'admin'){
  23. if(usermap[username].pass === password){
  24. return usermap['admin']
  25. }else{
  26. return []
  27. }
  28. }
  29. return usermap[username]
  30. },
  31. getUserInfo: config => {
  32. console.log('getUserInfo config: ', config)
  33. const { token } = param2Obj(config.url)
  34. let tok = false
  35. for(let key in usermap){
  36. if(token.indexOf(usermap[key].token) !== -1){
  37. tok = usermap[key]
  38. break;
  39. }
  40. }
  41. return tok
  42. },
  43. logout: () => 'success'
  44. }

5 vuex 的使用

src/store/index.js

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. import user from './modules/user'
  4. import permission from './modules/permissions'
  5. import getters from './getters'
  6. Vue.use(Vuex)
  7. const store = new Vuex.Store({
  8. modules: {
  9. permission,
  10. user,
  11. },
  12. getters
  13. })
  14. export default store

src/store/modules/user.js

  1. import { loginByUsernameApi, logoutApi, getUserInfoApi } from '@/api/login'
  2. import { getToken, setToken, removeToken } from '@/utils/auth'
  3. const user = {
  4. state: {
  5. token: getToken(),
  6. name: '',
  7. avatar: '',
  8. roles: []
  9. },
  10. mutations: {
  11. SET_TOKEN: (state, token) => {
  12. state.token = token
  13. },
  14. SET_NAME: ( state, name) => {
  15. state.name = name
  16. },
  17. SET_AVATAR: (state, avatar) => {
  18. state.avatar = avatar
  19. },
  20. SET_ROLES: (state, roles) => {
  21. state.roles = roles
  22. }
  23. },
  24. actions: {
  25. // 登入
  26. LoginByUsername({commit}, userinfo){
  27. const username = userinfo.username.trim()
  28. return new Promise((resolve, reject) => {
  29. loginByUsernameApi(username, userinfo.password)
  30. .then(resp => {
  31. const data = resp.data
  32. setToken(data.token)
  33. console.log('in LoginByUsername, setToken: ', data.token)
  34. commit('SET_TOKEN', data.token)
  35. resolve()
  36. })
  37. .catch(err => {
  38. reject(err)
  39. })
  40. })
  41. },
  42. // 获取用户权限等
  43. GetUserInfo({commit, state}) {
  44. console.log('in GetUserInfo')
  45. return new Promise((resolve, reject) => {
  46. getUserInfoApi(state.token)
  47. .then(resp => {
  48. if(!resp.data){
  49. reject('error')
  50. }
  51. const data = resp.data
  52. if(data.roles && data.roles.length){
  53. commit('SET_ROLES', data.roles)
  54. }else {
  55. reject('getUserInfoApi: roles must be a non-null array!')
  56. }
  57. if(data.name){
  58. commit('SET_NAME', data.name)
  59. }
  60. if(data.avatar){
  61. commit('SET_AVATAR', data.avatar)
  62. }
  63. resolve(resp)
  64. })
  65. .catch(err => {
  66. reject(err)
  67. })
  68. })
  69. },
  70. // 登出
  71. LogOut({commit, state}){
  72. return new Promise((resolve, reject) => {
  73. logoutApi(state.token)
  74. .then(() => {
  75. commit('SET_TOKEN', '')
  76. commit('SET_ROLES', [])
  77. removeToken()
  78. resolve()
  79. })
  80. .catch(err => {
  81. reject(err)
  82. })
  83. })
  84. },
  85. // 前端登出
  86. FedLogOut({commit}){
  87. return new Promise(resolve => {
  88. commit('SET_TOKEN', '')
  89. removeToken()
  90. resolve()
  91. })
  92. }
  93. }
  94. }
  95. export default user

src/store/modules/permissions.js

  1. import { asyncRouterMap, constantRouterMap } from '@/router'
  2. /**
  3. * 通过 meta.role 判断是否与当前用户权限匹配
  4. */
  5. function hasRoles (roles, route){
  6. if(route.meta && route.meta.roles){
  7. return roles.some(role => route.meta.roles.includes(role))
  8. }else{
  9. return true
  10. }
  11. }
  12. /**
  13. * 递归过滤异步路由表,返回符合用户角色权限的路由表
  14. */
  15. function filterAsyncRouter(asyncRouterMap, roles){
  16. const accessedRouters = asyncRouterMap.filter(route => {
  17. // 404
  18. if(route.path === '*'){
  19. return true
  20. }else if(hasRoles(roles, route)){
  21. if(route.children && route.children.length){
  22. route.children = filterAsyncRouter(route.children, roles)
  23. }
  24. return true
  25. }
  26. return false
  27. })
  28. return accessedRouters
  29. }
  30. // 在有权限的路由表里,查找是否有到目标path的路由
  31. // 为了保持路由唯一性,拼接父子路由
  32. function hasDestRoute (froute, permitRouterMap, to) {
  33. let r = froute === '/' ? '' : froute
  34. return permitRouterMap.some(route => {
  35. let path = r + '/' + route.path
  36. if (to.path.indexOf(path) !== -1) {
  37. return true;
  38. }
  39. if (route.children && route.children.length) { //如果有孩子就遍历孩子
  40. return hasDestRoute(path, route.children, to)
  41. }
  42. })
  43. }
  44. const permission = {
  45. state: {
  46. routers: constantRouterMap,
  47. addRouters: [],
  48. sidebar_routers: {},
  49. },
  50. mutations: {
  51. SET_ROUTERS: (state, routers) => {
  52. state.addRouters = routers,
  53. state.routers = constantRouterMap.concat(routers)
  54. },
  55. SET_NOW_ROUTERS: (state, to) => {
  56. console.log('in SET_NOW_ROUTERS')
  57. // 由于首页重定向到 /dashboard,并且不参与权限控制,特殊处理
  58. if(to.path === '/dashboard'){
  59. let dashboard = state.routers.filter(v => v.path === '/' )
  60. state.sidebar_routers = dashboard[0]
  61. }else{
  62. // 递归访问 accessedRouters,找到包含to 的那个路由对象,设置给 sidebar_routers
  63. state.addRouters.forEach(e => {
  64. if (e.children && e.children.length) {
  65. if ( hasDestRoute(e.path, e.children, to)){
  66. if(state.sidebar_routers.path){
  67. // 存在 sidebar_routers 且与目标路由不同
  68. if(state.sidebar_routers.path !== e.path){
  69. state.sidebar_routers = e;
  70. }
  71. }else{
  72. state.sidebar_routers = e;
  73. }
  74. }
  75. }
  76. })
  77. }
  78. }
  79. },
  80. actions: {
  81. GenerateRoutes({commit}, data) {
  82. console.log('in GenerateRoutes')
  83. return new Promise(resolve => {
  84. const {roles} = data
  85. let accessedRouters
  86. if(roles.includes('admin')){
  87. accessedRouters = asyncRouterMap
  88. }else{
  89. accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
  90. }
  91. commit('SET_ROUTERS', accessedRouters)
  92. resolve()
  93. })
  94. },
  95. GenSidebarRoutes({commit}, data) {
  96. console.log('in GenSidebarRoutes')
  97. return new Promise(resolve => {
  98. commit('SET_NOW_ROUTERS', data)
  99. resolve()
  100. })
  101. }
  102. }
  103. }
  104. export default permission

src/store/getters.js

  1. const getters = {
  2. token: state => state.user.token,
  3. roles: state => state.user.roles,
  4. avatar: state => state.user.avatar,
  5. name: state => state.user.name,
  6. addRouters: state => state.permission.addRouters,
  7. permission_routers: state => state.permission.routers,
  8. sidebar_routers: state => state.permission.sidebar_routers,
  9. }
  10. export default getters

6 修改 login 页面

src/views/TheLogin.vue

  1. handleSubmit(event) {
  2. this.$refs.ruleForm2.validate((valid) => {
  3. if (valid) {
  4. this.logining = true;
  5. // 触发 vuex 中 LoginByUsername 事件
  6. const username = this.ruleForm2.username
  7. let password = md5(this.ruleForm2.password)
  8. if(this.hidePassword !== '' && this.ruleForm2.password === '********'){
  9. password = this.hidePassword
  10. }
  11. this.$store.dispatch('LoginByUsername', {'username': username, 'password': password})
  12. .then(() => {
  13. this.logining = false
  14. if(this.rememberme){
  15. this.setCookie(this.ruleForm2.username, password, 7)
  16. }else{
  17. this.clearCookie()
  18. }
  19. // 重定向到首页
  20. this.$router.push({ path: this.redirect || '/' })
  21. })
  22. .catch(err => {
  23. this.logining = false
  24. this.$alert(err, {
  25. type: 'warning',
  26. confirmButtonText: 'ok'
  27. })
  28. })
  29. } else {
  30. console.log('error submit!');
  31. return false;
  32. }
  33. })
  34. }

7 修改main 主入口

src/main.js

  1. /** ...*/
  2. import store from './store'
  3. import '@/login'
  4. import '@/mock'
  5. Vue.use(ElementUI)
  6. Vue.config.productionTip = false
  7. /* eslint-disable no-new */
  8. new Vue({
  9. el: '#app',
  10. router,
  11. store,
  12. render: h => h(App)
  13. })

8 关键的权限控制

src/login.js

  1. import router from './router'
  2. import NProgress from 'nprogress'
  3. import {clearRequest} from '@/utils/request'
  4. import axios from 'axios'
  5. import store from './store'
  6. function hasPermission(roles, permissionRoles){
  7. if(roles.indexOf('admin') >= 0){
  8. return true // admin 权限 直接通过
  9. }
  10. // 没有配置权限的菜单直接进入
  11. if(! permissionRoles){
  12. return true
  13. }
  14. return roles.some(role => permissionRoles.indexOf(role) >= 0)
  15. }
  16. const whiteList = ['/login', ] // 不重定向白名单
  17. router.beforeEach((to, from, next) => {
  18. console.log('to.path: ' + to.path)
  19. console.log('store.getters.token: ' + store.getters.token)
  20. // 切换路由时清空上个路由未完成的所有请求
  21. const cancelToken = axios.CancelToken
  22. clearRequest.source.cancel && clearRequest.source.cancel()
  23. clearRequest.source = cancelToken.source()
  24. if(whiteList.indexOf(to.path) !== -1){
  25. next()
  26. }else{
  27. if(store.getters.token){
  28. if(to.path === '/login'){
  29. next({path: '/'})
  30. NProgress.done()
  31. }else{
  32. // 拉取用户信息
  33. if(store.getters.roles.length === 0){
  34. store.dispatch('GetUserInfo')
  35. .then(resp => {
  36. const roles = resp.data.roles
  37. console.log('roles: ', roles)
  38. // 根据权限生成可访问的路由表
  39. store.dispatch('GenerateRoutes', {roles})
  40. .then(() => {
  41. // 动态添加路由表
  42. router.addRoutes(store.getters.addRouters)
  43. next({...to, replace: true}) // 确保 addRouters 已完成
  44. })
  45. })
  46. .catch(err => {
  47. store.dispatch('FedLogOut')
  48. .then(() => {
  49. console.log('认证失败,请重新登陆')
  50. next({path: '/login'})
  51. })
  52. })
  53. }else{
  54. console.log('call GenSidebarRoutes')
  55. store.dispatch('GenSidebarRoutes', to)
  56. .then(() => {
  57. if(hasPermission(store.getters.roles, to.meta.role)){
  58. next()
  59. }else{
  60. next({path: '/', query: {noGoBack: true}})
  61. }
  62. })
  63. }
  64. }
  65. }else{
  66. // 重定向到 /login
  67. next({path: '/login', query: {redirect: to.fullpath}})
  68. NProgress.done()
  69. }
  70. }
  71. })
  72. router.afterEach(() => {
  73. NProgress.done()
  74. })

目录结构

  1. App.vue
  2. login.js
  3. main.js
  4. ├─api
  5. login.js
  6. system.js
  7. ├─assets
  8. logo.png
  9. ├─components
  10. HelloWorld.vue
  11. Navbar.vue
  12. NavbarItem.vue
  13. Sidebar.vue
  14. SidebarItem.vue
  15. ├─containers
  16. Container.vue
  17. ├─mock
  18. article.js
  19. index.js
  20. login.js
  21. system.js
  22. ├─router
  23. index.js

  24. └─modules
  25. system.js
  26. ├─store
  27. getters.js
  28. index.js

  29. └─modules
  30. permissions.js
  31. user.js
  32. ├─styles
  33. animate.scss
  34. browser-prefix.scss
  35. index.scss
  36. ├─utils
  37. auth.js
  38. index.js
  39. request.js
  40. └─views
  41. 404.vue
  42. Home.vue
  43. TheLogin.vue
  44. ├─article
  45. index.vue
  46. ├─dashboard
  47. index.vue
  48. ├─SidebarMenu
  49. index.vue
  50. └─system
  51. └─permit
  52. account.vue
  53. accountgroup.vue
  54. authorize.vue
  55. index.vue
  56. role.vue
  57. └─components
  58. DialogRoleMenu.vue

admin 用户权限:

developer 权限:

vue+elementui搭建后台管理界面(7 vuex和mockjs的使用)的更多相关文章

  1. vue+elementui搭建后台管理界面(6登录和菜单权限控制)

    不同的权限对应不同的路由(菜单),同时侧边栏也根据权限异步生成,实现登录和鉴权思路如下: 登录:点击登录,服务器验证通过后返回一个 token ,然后存到 cookie,再根据 token 拉取用户权 ...

  2. vue+elementui搭建后台管理界面(2首页)

    1 会话存储 使用html5的 sessionStorage 对象临时保存会话 // 保存会话 sessionStorage.setItem('user', username) // 删除会话 ses ...

  3. vue+elementui搭建后台管理界面

    1 会话存储 使用html5的 sessionStorage 对象临时保存会话 // 保存会话 sessionStorage.setItem('user', username) // 删除会话 ses ...

  4. vue+elementui搭建后台管理界面(1登录)

    1 node环境安装 从 node官网下载安装包 2 vue-cli npm install vue-cli -g 3 新建项目 vue init webpack vue-project 可保持默认, ...

  5. vue+elementui搭建后台管理界面(8 同步/异步获取数据渲染table)

    elementui已经封装好了 el-table 组件,只需要指定 data 数据源即可,因此通常在 vue 实例生命周期的 created 阶段,从数据库获取数据,再将返回的数据绑定到 data 如 ...

  6. vue+elementui搭建后台管理界面(5递归生成侧栏路由)

    有一个菜单树,顶层菜单下面有多个子菜单,子菜单下还有子菜单... 这时候就要用递归处理 1 定义多级菜单 修改 src/router/index.js 的 / 路由 { path: '/', redi ...

  7. vue+elementui搭建后台管理界面(3侧边栏菜单)

    上一节搭好了主框架,但是标签页和侧边栏只是分别展示了各自的菜单,如何将二者联动起来? 定义路由规则:当有 children 属性时,从 children 里取出 path 填充到侧边栏,如: { pa ...

  8. vue+elementui搭建后台管理界面(6登录和菜单权限控制[二])

    根据权限计算路由的代码 /** * 通过meta.role判断是否与当前用户权限匹配 * @param roles * @param route */ function hasRoles (roles ...

  9. vue+elementui搭建后台管理界面(4使用font-awesome)

    使用font-awesome npm install --save font-awesome 修改 src/main.js 增加 import 'font-awesome/scss/font-awes ...

随机推荐

  1. SpringCloud-Zuul源码分析和路由改造

    在使用SpringCloud的时候准备使用Zuul作为微服务的网关,Zuul的默认路由方式主要是两种,一种是在配置 文件里直接指定静态路由,另一种是根据注册在Eureka的服务名自动匹配.比如如果有一 ...

  2. mysql的innodb数据存储结构

    ​ 数据库磁盘读取与系统磁盘读取 1,系统从磁盘中读取数据到内存时是以磁盘块(block)为基本单位,位于同一个磁盘块中的数据会被一次性读取出来. 2,innodb存储引擎中有页(Page)的概念,页 ...

  3. node基础学习——http基础知识-02-http响应数据流

    <一> 发送服务器端响应流 在createServer()方法的参数值回调函数或服务器对象的request事件函数中的第二个参数值为一个http.ServerResponse对象,可以利用 ...

  4. 使用python的jira库操作jira的版本单和问题单链接

    操作JIRA的API来实现的. 但感觉比单纯操作API要简单一些. from jira import JIRA from django.conf import settings JIRA_URL = ...

  5. vue中引入.svg图标,使用iconfont图标库

    阿里巴巴的iconfont是一个很好的图标库,海量的素材可以快速满足开发人员日常对图标的诉求,我们采用symbol引用,官方介绍 创建SvgIcon组件 <template> <sv ...

  6. 代码编辑器与IDE(集成开发环境)

    编辑器就是轻量级的只用于编辑代码: nodepad++, sublime, ...... IDE就是包含很多例如调试, 编译,UI界面的功能更为完善的软件: Pycharm(python用的多), V ...

  7. C#线程池 ThreadPool

    什么是线程池 大家都知道,我们在打开一个应用的时候,操作系统是要做很多的事情的,动态链接.装载.分配虚拟空间.等等等等,其实一个应用的打开同时也伴随着一个进程的建立. 进程的建立是需要时间的,在进程上 ...

  8. 使用docker搭建etcd

    下载etcd代码然后拷贝到服务器 来自为知笔记(Wiz)

  9. HBASE-LSM树(转载)

    HBASE-LSM树 1.B+树 关于B树.B+树.B树的了解参考:* http://blog.csdn.net/v_july_v/article/details/6530142 优点: 走进搜索引擎 ...

  10. UFUN函数 UF_CFI函数(uc4504,uc4540,uc4514,uc4547,UF_CFI_ask_file_exist )

    UF_initialize(); //指定本地数据文件的路径 char file_spec[]="D://Program Files//Siemens//NX 8.0//UGII//zyTO ...