web系统动态菜单及路由的方法如下:前端根据获取的用户角色信息,动态生成路由
1.后端
- 创建数据库表如下所示:包含用户表和角色表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for roles
-- ----------------------------
DROP TABLE IF EXISTS `roles`;
CREATE TABLE `roles` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称',
`code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色编码',
`description` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色描述',
`created_time` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `code`(`code` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`role_id` int NOT NULL COMMENT '角色ID',
`created_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 23 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1;
- 后端核心接口如下:主要是一个获取用户信息接口,这个接口会返回roles字段,表示请求接口的用户的角色信息。
@ApiOperation("获取用户信息")
@ApiImplicitParam(name = "Authorization", value = "Bearer token", required = true, paramType = "header")
@GetMapping("/info")
public Result<Map<String, Object>> getUserInfo(@RequestHeader("Authorization") String token) {
try {
String identifier = jwtUtil.getIdentifierFromToken(token);
LoginType loginType = jwtUtil.getLoginTypeFromToken(token);
User user;
if (LoginType.EMAIL.equals(loginType)) {
user = userService.getByEmail(identifier).get();
} else {
user = userService.getByPhone(identifier).get();
}
if (user != null) {
Map<String, Object> userInfo = new HashMap<>();
String roleCode = roleService.getRoleCode(user.getRoleId());
userInfo.put("roles", new String[]{roleCode});
userInfo.put("name", user.getUsername());
userInfo.put("avatar", user.getAvatar());
userInfo.put("userId", user.getId());
userInfo.put("createTime", user.getCreatedTime());
userInfo.put("email", user.getEmail());
userInfo.put("phone", user.getPhone());
return Result.success(userInfo);
}
return Result.error("用户不存在");
} catch (Exception e) {
log.error("获取用户信息失败", e);
return Result.error("获取用户信息失败");
}
}
2.前端
- 在src/router/index.js中创建静态路由和动态路由:
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
/* Layout */
import Layout from "@/layout";
// 静态路由,所有角色都可以访问
export const constantRoutes = [
{
path: "/login",
component: () => import("@/views/login/index"),
hidden: true,
},
{
path: "/404",
component: () => import("@/views/404"),
hidden: true,
},
{
path: "/",
component: Layout,
redirect: "/dashboard",
children: [
{
path: "dashboard",
name: "Dashboard",
component: () => import("@/views/dashboard/index"),
meta: { title: "首页", icon: "dashboard" },
},
],
},
{
path: "/register",
component: () => import("@/views/register/index"),
hidden: true,
},
{
path: "/profile",
component: Layout,
hidden: true,
children: [
{
path: "",
component: () => import("@/views/profile/index"),
name: "Profile",
meta: { title: "更改密码" },
},
],
},
];
// asyncRoutes 动态路由
export const asyncRoutes = [
{
path: "/user",
component: Layout,
meta: {
roles: ['ADMIN', 'SUPER_ADMIN']
},
children: [
{
path: "index",
name: "User",
component: () => import("@/views/user/index"),
meta: { title: "用户管理", icon: "el-icon-user",roles: ['ADMIN', 'SUPER_ADMIN'] },
},
],
},
{
path: "/service",
component: Layout,
meta: {
roles: ['SUPER_ADMIN']
},
children: [
{
path: "index",
name: "Service",
component: () => import("@/views/service/index"),
meta: { title: "服务配置", icon: "el-icon-setting", roles: ['SUPER_ADMIN'] },
},
],
},
{
path: "/fingerprintMatch",
component: Layout,
meta: {
roles: ['NORMAL', 'ADMIN', 'SUPER_ADMIN']
},
children: [
{
path: "index",
name: "FingerprintMatch",
component: () => import("@/views/FingerprintMatch/index"),
meta: { title: "指纹比对", icon: "el-icon-camera", roles: ['NORMAL', 'ADMIN', 'SUPER_ADMIN'] },
},
],
},
{
path: "/fingerprintManage",
component: Layout,
meta: {
roles: [ 'ADMIN', 'SUPER_ADMIN']
},
children: [
{
path: "index",
name: "FingerprintManage",
component: () => import("@/views/FingerprintManage/index"),
meta: { title: "指纹管理", icon: "el-icon-document",roles: ['ADMIN', 'SUPER_ADMIN'] },
},
],
},
{
path: "/history",
component: Layout,
meta: {
roles: [ 'ADMIN', 'SUPER_ADMIN', 'NORMAL']
},
children: [
{
path: "index",
name: "History",
component: () => import("@/views/history/index"),
meta: { title: "比对记录", icon: "el-icon-time",roles: ['ADMIN', 'SUPER_ADMIN', 'NORMAL'] },
},
{
path: "detail/:id",
name: "HistoryDetail",
component: () => import("@/views/history/detail"),
meta: { title: "比对详情",roles: ['ADMIN', 'SUPER_ADMIN', 'NORMAL'] },
hidden: true,
},
],
},
];
const createRouter = () =>
new Router({
mode: 'history', // 去掉url中的#
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes,
});
const router = createRouter();
// 重置路由函数
export function resetRouter() {
const newRouter = createRouter();
router.matcher = newRouter.matcher;
}
export default router;
- 在src\store\modules\permission.js中定义如下:
import { asyncRoutes, constantRoutes } from '@/router'
/**
* 使用用户角色和route.meta.roles判断用户是否有路由权限
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* 过滤路由表,将有权限的路由过滤出来
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [], // 可访问路由
addRoutes: [] // 动态添加的路由
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, data ) {
return new Promise(resolve => {
const { roles } = data;
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
- 在src\store\modules\user.js中定义如下:
import { login, logout, getInfo, register, sendVerifyCode } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'
const getDefaultState = () => {
return {
token: getToken(),
name: '',
avatar: '',
userId: '',
roles: []
}
}
const state = getDefaultState()
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_USER_ID: (state, id) => {
state.userId = id
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
const actions = {
// user login
login({ commit }, userInfo) {
const { email, phone, password, loginType } = userInfo
return new Promise((resolve, reject) => {
login({ email, phone, password, loginType }).then(response => {
const { data } = response
const { token} = data
commit('SET_TOKEN', token)
setToken(token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
return reject('Verification failed, please Login again.')
}
const { name, avatar, userId, roles } = data
const rolesArray = typeof roles === 'string' ? JSON.parse(roles) : roles
if (!rolesArray || rolesArray.length <= 0) {
reject('getInfo: roles must be a non-null array!')
}
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_USER_ID', userId)
commit('SET_ROLES', rolesArray)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
removeToken()
resetRouter()
commit('RESET_STATE')
resolve()
}).catch(error => {
reject(error)
})
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
removeToken()
commit('RESET_STATE')
resolve()
})
},
// 发送验证码
sendVerifyCode({ commit }, data) {
return new Promise((resolve, reject) => {
sendVerifyCode(data).then(response => {
resolve(response)
}).catch(error => {
reject(error)
})
})
},
// 注册
register({ commit }, data) {
return new Promise((resolve, reject) => {
register(data).then(response => {
resolve(response)
}).catch(error => {
reject(error)
})
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
- src\store\getters.js:
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
roles: state => state.user.roles,
addRouters: state => state.permission.addRoutes
}
export default getters
- src\store\index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
import permission from './modules/permission'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app, // 管理应用级别的状态
settings, // 管理应用设置
user, // 管理用户状态
permission// 管理权限和路由
},
getters
})
export default store
- src\permission.js:
import router from "./router";
import store from "./store";
import { Message } from "element-ui";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { getToken } from "@/utils/auth";
import getPageTitle from "@/utils/get-page-title";
NProgress.configure({ showSpinner: false });
const whiteList = ["/login", "/register"];
router.beforeEach(async (to, from, next) => {
NProgress.start();
document.title = getPageTitle(to.meta.title);
const hasToken = getToken();
if (hasToken) {
if (to.path === "/login") {
next({ path: "/" });
} else {
// 判断当前用户是否包含角色信息
if (store.getters.roles.length === 0) {
store.dispatch("user/getInfo").then((res) => {
const roles = res.roles;
store.dispatch("permission/generateRoutes", {roles}).then(() => {
router.addRoutes(store.getters.addRouters);
// hack方法 确保addRoutes已完成
next({ ...to, replace: true });
});
})
.catch((err) => {
console.log(err);
});
} else {
next();
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
next(`/login`);
NProgress.done();
}
}
});
router.afterEach(() => {
NProgress.done();
});
- src\layout\components\Sidebar\index.vue下:
routes() {
return this.$store.state.permission.routes
}
浙公网安备 33010602011771号