码云地址:
https://gitee.com/chu1204505056/vue-admin-better
其登录逻辑为:
1、登录拿取到token,分配到vuex中;
2、通过token拿取到用户的permission角色名称,用户头像,昵称,并存放到vuex中;
3、每次路由时候,调取配置的permission.js ,如果没有角色名称,则返回第二步,如果有,则通
过token向后台请求当前用户的权限路由,并对基础路由进行拼接,存放到vuex中进行状态管理;
调用 this.$store .dispatch('user/login', this.form)并返回值,
user/login这样调用是因为,store中导出namespaced为true,就可以根据简写找到vuex中actions的方法
export default {
namespaced: true,//引用需要的模块名称 /menu/GetUserMenu
state,
mutations,
actions
}
- login() {
- this.loading = true
- this.$store
- .dispatch('user/login', this.form)
- .then(() => {
- const routerPath = '/'
- this.$router.push(routerPath).catch(() => {})
- this.loading = false
- })
- .catch(() => {
- this.loading = false
- })
- },
import { getUserInfo, login, logout } from '@/api/userRegistration'
调用自己定义的登录接口,主要返回token
- async login({ commit }, userInfo) {
- const { data } = await login(userInfo)
- const accessToken = data.access_token;
- const setUserInfo=data.userInfo
- if (accessToken) {
- commit('setAccessToken', accessToken)
- commit('setUserInfo',setUserInfo)
- const hour = new Date().getHours()
- console.log(hour)
- const thisTime =
- hour < 8
- ? '早上好'
- : hour <= 11
- ? '上午好'
- : hour <= 13
- ? '中午好'
- : hour < 18
- ? '下午好'
- : '晚上好'
- Vue.prototype.$baseNotify(`欢迎登录${title}`, `${thisTime}!`)
- } else {
- Vue.prototype.$baseMessage(
- `登录接口异常,未正确返回${tokenName}...`,
- 'error'
- )
- }
- },
登录接口
我后台采用 Spring Scurity的oauth2进行登录,接口如下
- // 数据格式
- const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
- // 请求头添加 Authorization: Basic client_id:client_secret
- const auth = {
- username: 'mxg-blog-admin',
- password: '123456',
- }
-
- export function login(data) {
- return request({
- headers: headers,
- auth: auth,
- url: `/supplierAuth/login`,
- method: 'POST',
- params: data,
- })
- }
返回字段如下

采用如下,对每次路由进行拦截
router.beforeResolve(async (to, from, next) => {
逻辑还是比较好理解
1、判断是否有token,判断路由是否在白名单
2、判断是否有权限角色(user/permissions)存储
3、如果没有则获取用户权限,获取路由并拼接,如果存在,则直接访问
- router.beforeResolve(async (to, from, next) => {
- if (progressBar) VabProgress.start()
- let hasToken = store.getters['user/accessToken']
-
-
- if (hasToken) {
- if (to.path === '/login') {
- next({ path: '/' })
- if (progressBar) VabProgress.done()
- } else {
- const hasPermissions =
- store.getters['user/permissions'] &&
- store.getters['user/permissions'].length > 0
- if (hasPermissions) {
- next()
- } else {
- try {
- let permissions
- if (!loginInterception) {
- //settings.js loginInterception为false时,创建虚拟权限
- await store.dispatch('user/setPermissions', ['admin'])
- permissions = ['admin']
- } else {
- permissions = await store.dispatch('user/getUserInfo')
- }
-
- let accessRoutes = []
- if (authentication === 'intelligence') {
- accessRoutes = await store.dispatch('routes/setRoutes', permissions)
- } else if (authentication === 'all') {
- // console.log("all")
- console.log('拿取到用户信息')
- accessRoutes = await store.dispatch('routes/setAllRoutes')
- }
- console.log(accessRoutes)
- accessRoutes.forEach((item) => {
- console.log(item)
- router.addRoute(item)
- })
- next({ ...to, replace: true })
- } catch {
- await store.dispatch('user/resetAccessToken')
- if (progressBar) VabProgress.done()
- }
- }
- }
- } else {
- console.log(routesWhiteList)
- if (routesWhiteList.indexOf(to.path) !== -1) {
- next()
- } else {
- if (recordRoute) {
- next(`/login?redirect=${to.path}`)
- } else {
- next('/login')
- }
-
- if (progressBar) VabProgress.done()
- }
- }
- document.title = getPageTitle(to.meta.title)
- })
- router.afterEach(() => {
- if (progressBar) VabProgress.done()
- })
获取用户信息
permissions = await store.dispatch('user/getUserInfo')
获取用户信息
- async getUserInfo({ commit, state }) {
- const { data } = await getUserInfo(state.accessToken)
- if (!data) {
- Vue.prototype.$baseMessage('验证失败,请重新登录...', 'error')
- return false
- }
- let { permissions, username, avatar } = data
- if (permissions && username && Array.isArray(permissions)) {
- commit('setPermissions', permissions)
- commit('setUsername', username)
- commit('setAvatar', avatar)
- return permissions
- } else {
- Vue.prototype.$baseMessage('用户信息接口异常', 'error')
- return false
- }
- },
接口如下 ,这个后台可以根据token,自由发挥
- export function getUserInfo(accessToken) {
- return request({
- url: '/supplierSystem/menu/userPermission',
- method: 'post',
- data: {
- "accessToken": accessToken,
- },
- })
- }
获取在permission.js中路由,并动态添加到route中
- accessRoutes = await store.dispatch('routes/setAllRoutes')
- console.log(accessRoutes)
- accessRoutes.forEach((item) => {
- console.log(item)
- router.addRoute(item)
- })
在store中找到routes/setAllRoutes方法,获取后台当前用户路由,这个路由,后台要按照前端方式拼接
这个是源代码的设置路由
- async setAllRoutes({ commit }) {
- let { data } = await getRouterList()
- data.push({ path: '*', redirect: '/404', hidden: true })
- let accessRoutes = convertRouter(data)
- console.log(accessRoutes);
- commit('setAllRoutes', accessRoutes)
-
- return accessRoutes
- },
这个是我的设置的路由。主要是加入了按钮的权限集合在里面
- async setAllRoutes({ commit }) {
- let { data } = await getRouterList()
- let dataRoute = data.menuTreeList
- dataRoute.push({ path: '*', redirect: '/404', hidden: true })
- let accessRoutes = convertRouter(dataRoute)
- commit('setAllRoutes', accessRoutes)
- commit('setButtonList', data.buttonList)
- return accessRoutes
- },
- const state = () => ({
- routes: [],
- partialRoutes: [],
- buttonList: [],
- })
- const getters = {
- routes: (state) => state.routes,
- partialRoutes: (state) => state.partialRoutes,
- buttonList: (state) => state.buttonList,
- }
- const mutations = {
- setRoutes(state, routes) {
- state.routes = constantRoutes.concat(routes)
- },
- setAllRoutes(state, routes) {
- state.routes = constantRoutes.concat(routes)
- },
- setButtonList(state, buttonList) {
- state.buttonList = buttonList
- },
- setPartialRoutes(state, routes) {
- state.partialRoutes = constantRoutes.concat(routes)
- },
- }
后端构建的菜单结构模式如下
- {
- path: '/',
- component: 'Layout',
- redirect: 'index',
- children: [
- {
- path: 'index',
- name: 'Index',
- component: '@/views/index/index',
- meta: {
- title: '首页',
- icon: 'home',
- affix: true,
- },
- },
- ],
- },
- {
- path: '/personnelManagement',
- component: 'Layout',
- redirect: 'noRedirect',
- name: 'PersonnelManagement',
- meta: { title: '人员', icon: 'users-cog', permissions: ['admin'] },
- children: [
- {
- path: 'userManagement',
- name: 'UserManagement',
- component: '@/views/personnelManagement/userManagement/index',
- meta: { title: '用户管理' },
- },
- {
- path: 'roleManagement',
- name: 'RoleManagement',
- component: '@/views/personnelManagement/roleManagement/index',
- meta: { title: '角色管理' },
- },
- {
- path: 'menuManagement',
- name: 'MenuManagement',
- component: '@/views/personnelManagement/menuManagement/index',
- meta: { title: '菜单管理', badge: 'New' },
- },
- ],
- },
后端测试构建菜单 (没有加按钮权限集合)
- @Override
- public Result selectMenuThreeByUserIdTwo(String userId) {
- ArrayList<Object> arrayList = new ArrayList<>();
- HashMap<String, Object> hashMap = new HashMap<>();
- hashMap.put("path", "/");
- hashMap.put("component", "Layout");
- hashMap.put("redirect", "index");
- LinkedList<Object> children = new LinkedList<>();
- HashMap<String, Object> hashMap1 = new HashMap<>();
- hashMap1.put("path", "index");
- hashMap1.put("name", "Index");
- hashMap1.put("component", "@/views/index/index");
- HashMap<Object, Object> hashMap2 = new HashMap<>();
- hashMap2.put("title", "首页");
- hashMap1.put("meta", hashMap2);
- children.add(hashMap1);
-
- hashMap.put("children", children);
- arrayList.add(hashMap);
-
-
- HashMap<String, Object> hashMap11 = new HashMap<>();
- hashMap11.put("path", "/personnelManagement");
- hashMap11.put("component", "Layout");
- hashMap11.put("redirect", "noRedirect");
-
- hashMap11.put("name", "PersonnelManagement");
-
- HashMap<Object, Object> hashMap22 = new HashMap<>();
- hashMap22.put("title", "人员管理");
- hashMap11.put("meta", hashMap22);
- ArrayList<Object> arrayList2 = new ArrayList<>();
- HashMap<Object, Object> hashMap3 = new HashMap<>();
- hashMap3.put("path", "userManagement");
- hashMap3.put("name", "UserManagement");
- hashMap3.put("component", "@/views/personnelManagement/userManagement/index");
-
- HashMap<Object, Object> hashMap4 = new HashMap<>();
- hashMap4.put("title", "人员管理");
-
- hashMap3.put("meta", hashMap4);
- arrayList2.add(hashMap3);
- HashMap<Object, Object> hashMap5 = new HashMap<>();
- hashMap5.put("path", "roleManagement");
- hashMap5.put("name", "roleManagement");
- hashMap5.put("component", "@/views/personnelManagement/roleManagement/index");
-
- HashMap<Object, Object> hashMap6 = new HashMap<>();
- hashMap6.put("title", "角色管理");
-
- hashMap5.put("meta", hashMap6);
- arrayList2.add(hashMap5);
-
- HashMap<Object, Object> hashMap7 = new HashMap<>();
- hashMap7.put("path", "roleManagement");
- hashMap7.put("name", "roleManagement");
- hashMap7.put("component", "@/views/personnelManagement/roleManagement/index");
-
- HashMap<Object, Object> hashMap8 = new HashMap<>();
- hashMap8.put("title", "菜单管理");
- // hashMap8.put("badge","New");
- hashMap7.put("meta", hashMap6);
- arrayList2.add(hashMap7);
- hashMap11.put("children", arrayList2);
- arrayList.add(hashMap11);
- return Result.ok(arrayList);
- }
右边页面

通过如下导入的 vab-side-bar文件,如果你想自定义修改菜单栏,则可以找到下面修改

在nodemudle中可以查看下载的菜单文件,

前面在菜单和按钮集合存在vuex之后;配置自定义指令,来显示或者隐藏按钮
- // 引入所有要注册的全局指令
- import permission from './permission'
- // 将permission文件导出的对象引入
- export default (Vue) => {
- // 第一个参数为指令名称,v-permission 使用,
- // 注意在生命指令名时候,不能加v-
- Vue.directive('permission', permission)
- }
- // 然后在main.js 中全局引入这个文件
permission .js如下
- //自定义权限指令
- import store from '@/store/index'
- // 导出一个对象
- export default {
- // 指令钩子
- inserted(el, binding) {
- //el 指令作用到哪个元素上
- //获取使用指令时传递的值
- const { value } = binding
- //获取用户当前所拥有的按钮权限
- // store.getters['user/permissions']
- // const buttonList = store.getters && store.getters.buttonList
- const buttonList = store.getters['routes/buttonList']
-
- if (value) {
- // 用some,不用foreach 因为如果下面为true,则进行终止循环
- const hasPermission = buttonList.some((button) => {
- return button === value
- })
- // 如果没有权限,则将元素移除
- if (!hasPermission) {
- el.parentNode && el.parentNode.removeChild(el)
- }
- } else {
- throw new Error("需要权限标识!比如v-permission='article:delete'")
- }
- },
- }

在main.js中引入这个自定义指令,
import directive from '@/directive' //导入自定义指令
Vue.use(directive)
使用如下
- <el-button
- type="primary"
- @click="handleQuery"
- v-permission="'user:search'"
- >
- 搜索
- </el-button>