將權限管理應用到系統,首先作好登陸, 點擊登陸按鈕後,觸發如下動做css
使用axios 封裝好請求和響應前端
src/utils/request.jsvue
import axios from 'axios' const clearRequest = { source: { token: null, cancel: null } } const cancelToken = axios.CancelToken const source = cancelToken.source() // 建立 axios 實例 const service = axios.create({ cancelToken: source.token, timeout: 6000, // 請求超時時間 }) // request 攔截器 service.interceptors.request.use( config => { config.cancelToken = clearRequest.source.token return config }, error => { return Promise.reject(error) } ) // response 攔截器 service.interceptors.response.use( resp => resp, error => { return Promise.reject(error) } ) export { clearRequest } export default service
src/utils/index.jsios
export function param2Obj(url){ const search = url.split('?')[1] if(!search){ return {} } return JSON.parse( '{"' + decodeURIComponent(search) .replace(/"/g, '\\"') .replace(/&/g, '","') .replace(/=/g, '":"') + '"}' ) }
src/utils/auth.jsgit
import Cookies from 'js-cookie' const tokenKey = 'X-Token' export function getToken(){ return Cookies.get(tokenKey) } export function setToken(token){ return Cookies.set(tokenKey, token) } export function removeToken(){ return Cookies.remove(tokenKey) }
src/api/login.js 文件github
import request from '@/utils/request' export function loginByUsernameApi(username, password){ return request({ url: '/api/auth/api-token-auth', method: 'post', data: { username, password } }) } export function getUserInfoApi(token){ return request({ url: '/api/userinfo', method: 'get', params: {token} }) } export function logoutApi(){ return request({ url: '/api/auth/logout', method: 'post' }) }
src/mock/index.jsvuex
import Mock from 'mockjs' import login from './login' // 登陸相關 Mock.mock(/\/api\/auth\/api-token-auth/, 'post', login.loginByUsername) Mock.mock(/\/api\/auth\/logout/, 'post', login.logout) Mock.mock(/\/api\/userinfo/, 'get', login.getUserInfo) export default Mock
src/mock/login.jsaxios
import { param2Obj } from '@/utils' const usermap = { admin: { token: 'admin', introduction: '我是超級管理員', name: 'Super Admin', pass: 'e10adc3949ba59abbe56e057f20f883e', roles: ['admin'] }, developer: { token: 'developer', introduction: '我是開發', name: '工程師小王', pass: 'e10adc3949ba59abbe56e057f20f883e', roles: ['/system', '/system/permit', '/system/permit/account'] } } export default { loginByUsername: config => { const { username, password } = JSON.parse(config.body) console.log('loginByUsername username, password: ', username, password) if(username === 'admin'){ if(usermap[username].pass === password){ return usermap['admin'] }else{ return [] } } return usermap[username] }, getUserInfo: config => { console.log('getUserInfo config: ', config) const { token } = param2Obj(config.url) let tok = false for(let key in usermap){ if(token.indexOf(usermap[key].token) !== -1){ tok = usermap[key] break; } } return tok }, logout: () => 'success' }
src/store/index.js後端
import Vue from 'vue' import Vuex from 'vuex' import user from './modules/user' import permission from './modules/permissions' import getters from './getters' Vue.use(Vuex) const store = new Vuex.Store({ modules: { permission, user, }, getters }) export default store
src/store/modules/user.jsapi
import { loginByUsernameApi, logoutApi, getUserInfoApi } from '@/api/login' import { getToken, setToken, removeToken } from '@/utils/auth' const user = { state: { token: getToken(), name: '', avatar: '', roles: [] }, mutations: { SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: ( state, name) => { state.name = name }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }, SET_ROLES: (state, roles) => { state.roles = roles } }, actions: { // 登入 LoginByUsername({commit}, userinfo){ const username = userinfo.username.trim() return new Promise((resolve, reject) => { loginByUsernameApi(username, userinfo.password) .then(resp => { const data = resp.data setToken(data.token) console.log('in LoginByUsername, setToken: ', data.token) commit('SET_TOKEN', data.token) resolve() }) .catch(err => { reject(err) }) }) }, // 獲取用戶權限等 GetUserInfo({commit, state}) { console.log('in GetUserInfo') return new Promise((resolve, reject) => { getUserInfoApi(state.token) .then(resp => { if(!resp.data){ reject('error') } const data = resp.data if(data.roles && data.roles.length){ commit('SET_ROLES', data.roles) }else { reject('getUserInfoApi: roles must be a non-null array!') } if(data.name){ commit('SET_NAME', data.name) } if(data.avatar){ commit('SET_AVATAR', data.avatar) } resolve(resp) }) .catch(err => { reject(err) }) }) }, // 登出 LogOut({commit, state}){ return new Promise((resolve, reject) => { logoutApi(state.token) .then(() => { commit('SET_TOKEN', '') commit('SET_ROLES', []) removeToken() resolve() }) .catch(err => { reject(err) }) }) }, // 前端登出 FedLogOut({commit}){ return new Promise(resolve => { commit('SET_TOKEN', '') removeToken() resolve() }) } } } export default user
src/store/modules/permissions.js
import { asyncRouterMap, constantRouterMap } from '@/router' /** * 經過 meta.role 判斷是否與當前用戶權限匹配 */ function hasRoles (roles, route){ if(route.meta && route.meta.roles){ return roles.some(role => route.meta.roles.includes(role)) }else{ return true } } /** * 遞歸過濾異步路由表,返回符合用戶角色權限的路由表 */ function filterAsyncRouter(asyncRouterMap, roles){ const accessedRouters = asyncRouterMap.filter(route => { // 404 if(route.path === '*'){ return true }else if(hasRoles(roles, route)){ if(route.children && route.children.length){ route.children = filterAsyncRouter(route.children, roles) } return true } return false }) return accessedRouters } // 在有權限的路由表裏,查找是否有到目標path的路由 // 爲了保持路由惟一性,拼接父子路由 function hasDestRoute (froute, permitRouterMap, to) { let r = froute === '/' ? '' : froute return permitRouterMap.some(route => { let path = r + '/' + route.path if (to.path.indexOf(path) !== -1) { return true; } if (route.children && route.children.length) { //若是有孩子就遍歷孩子 return hasDestRoute(path, route.children, to) } }) } const permission = { state: { routers: constantRouterMap, addRouters: [], sidebar_routers: {}, }, mutations: { SET_ROUTERS: (state, routers) => { state.addRouters = routers, state.routers = constantRouterMap.concat(routers) }, SET_NOW_ROUTERS: (state, to) => { console.log('in SET_NOW_ROUTERS') // 因爲首頁重定向到 /dashboard,而且不參與權限控制,特殊處理 if(to.path === '/dashboard'){ let dashboard = state.routers.filter(v => v.path === '/' ) state.sidebar_routers = dashboard[0] }else{ // 遞歸訪問 accessedRouters,找到包含to 的那個路由對象,設置給 sidebar_routers state.addRouters.forEach(e => { if (e.children && e.children.length) { if ( hasDestRoute(e.path, e.children, to)){ if(state.sidebar_routers.path){ // 存在 sidebar_routers 且與目標路由不一樣 if(state.sidebar_routers.path !== e.path){ state.sidebar_routers = e; } }else{ state.sidebar_routers = e; } } } }) } } }, actions: { GenerateRoutes({commit}, data) { console.log('in GenerateRoutes') return new Promise(resolve => { const {roles} = data let accessedRouters if(roles.includes('admin')){ accessedRouters = asyncRouterMap }else{ accessedRouters = filterAsyncRouter(asyncRouterMap, roles) } commit('SET_ROUTERS', accessedRouters) resolve() }) }, GenSidebarRoutes({commit}, data) { console.log('in GenSidebarRoutes') return new Promise(resolve => { commit('SET_NOW_ROUTERS', data) resolve() }) } } } export default permission
src/store/getters.js
const getters = { token: state => state.user.token, roles: state => state.user.roles, avatar: state => state.user.avatar, name: state => state.user.name, addRouters: state => state.permission.addRouters, permission_routers: state => state.permission.routers, sidebar_routers: state => state.permission.sidebar_routers, } export default getters
src/views/TheLogin.vue
handleSubmit(event) { this.$refs.ruleForm2.validate((valid) => { if (valid) { this.logining = true; // 觸發 vuex 中 LoginByUsername 事件 const username = this.ruleForm2.username let password = md5(this.ruleForm2.password) if(this.hidePassword !== '' && this.ruleForm2.password === '********'){ password = this.hidePassword } this.$store.dispatch('LoginByUsername', {'username': username, 'password': password}) .then(() => { this.logining = false if(this.rememberme){ this.setCookie(this.ruleForm2.username, password, 7) }else{ this.clearCookie() } // 重定向到首頁 this.$router.push({ path: this.redirect || '/' }) }) .catch(err => { this.logining = false this.$alert(err, { type: 'warning', confirmButtonText: 'ok' }) }) } else { console.log('error submit!'); return false; } }) }
src/main.js
/** ...*/ import store from './store' import '@/login' import '@/mock' Vue.use(ElementUI) Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, store, render: h => h(App) })
src/login.js
import router from './router' import NProgress from 'nprogress' import {clearRequest} from '@/utils/request' import axios from 'axios' import store from './store' function hasPermission(roles, permissionRoles){ if(roles.indexOf('admin') >= 0){ return true // admin 權限 直接經過 } // 沒有配置權限的菜單直接進入 if(! permissionRoles){ return true } return roles.some(role => permissionRoles.indexOf(role) >= 0) } const whiteList = ['/login', ] // 不重定向白名單 router.beforeEach((to, from, next) => { console.log('to.path: ' + to.path) console.log('store.getters.token: ' + store.getters.token) // 切換路由時清空上個路由未完成的全部請求 const cancelToken = axios.CancelToken clearRequest.source.cancel && clearRequest.source.cancel() clearRequest.source = cancelToken.source() if(whiteList.indexOf(to.path) !== -1){ next() }else{ if(store.getters.token){ if(to.path === '/login'){ next({path: '/'}) NProgress.done() }else{ // 拉取用戶信息 if(store.getters.roles.length === 0){ store.dispatch('GetUserInfo') .then(resp => { const roles = resp.data.roles console.log('roles: ', roles) // 根據權限生成可訪問的路由表 store.dispatch('GenerateRoutes', {roles}) .then(() => { // 動態添加路由表 router.addRoutes(store.getters.addRouters) next({...to, replace: true}) // 確保 addRouters 已完成 }) }) .catch(err => { store.dispatch('FedLogOut') .then(() => { console.log('認證失敗,請從新登錄') next({path: '/login'}) }) }) }else{ console.log('call GenSidebarRoutes') store.dispatch('GenSidebarRoutes', to) .then(() => { if(hasPermission(store.getters.roles, to.meta.role)){ next() }else{ next({path: '/', query: {noGoBack: true}}) } }) } } }else{ // 重定向到 /login next({path: '/login', query: {redirect: to.fullpath}}) NProgress.done() } } }) router.afterEach(() => { NProgress.done() })
目錄結構
│ App.vue │ login.js │ main.js ├─api │ login.js │ system.js ├─assets │ logo.png ├─components │ HelloWorld.vue │ Navbar.vue │ NavbarItem.vue │ Sidebar.vue │ SidebarItem.vue ├─containers │ Container.vue ├─mock │ article.js │ index.js │ login.js │ system.js ├─router │ │ index.js │ │ │ └─modules │ system.js ├─store │ │ getters.js │ │ index.js │ │ │ └─modules │ permissions.js │ user.js ├─styles │ animate.scss │ browser-prefix.scss │ index.scss ├─utils │ auth.js │ index.js │ request.js └─views │ 404.vue │ Home.vue │ TheLogin.vue ├─article │ index.vue ├─dashboard │ index.vue ├─SidebarMenu │ index.vue └─system └─permit │ account.vue │ accountgroup.vue │ authorize.vue │ index.vue │ role.vue └─components DialogRoleMenu.vue
admin 用戶權限:
developer 權限: