feat: 多角色登录权限控制
This commit is contained in:
parent
32fd8caa50
commit
47f7bc2623
@ -17,9 +17,9 @@ service.interceptors.request.use(
|
||||
if (localStorage.getItem("user-info")) {
|
||||
userInfo = JSON.parse(localStorage.getItem("user-info") as string);
|
||||
}
|
||||
if (userInfo?.AdminToken) {
|
||||
if (userInfo?.token) {
|
||||
// 有token,在请求头中携带token
|
||||
config.headers.Authorization = userInfo.AdminToken;
|
||||
config.headers.Authorization = userInfo.token;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
@ -53,7 +53,7 @@ service.interceptors.response.use(
|
||||
}
|
||||
},
|
||||
function (error: any) {
|
||||
localStorage.removeItem("AdminToken");
|
||||
localStorage.removeItem("token");
|
||||
router.push("/login");
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@ -112,6 +112,7 @@ import { useI18n } from "vue-i18n";
|
||||
import { Modal } from "@arco-design/web-vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useRoutesConfigStore } from "@/store/modules/route-config";
|
||||
import { useUserInfoStore } from "@/store/modules/user-info";
|
||||
import { useThemeConfig } from "@/store/modules/theme-config";
|
||||
import { useThemeMethods } from "@/hooks/useThemeMethods";
|
||||
@ -190,8 +191,12 @@ const logOut = () => {
|
||||
closable: true,
|
||||
onBeforeOk: async () => {
|
||||
try {
|
||||
// 用户退出
|
||||
const store = useUserInfoStore();
|
||||
await store.logOut();
|
||||
// 重置路由树
|
||||
const route = useRoutesConfigStore();
|
||||
await route.resetRoute();
|
||||
router.replace("/login");
|
||||
return true;
|
||||
} catch {
|
||||
|
||||
@ -19,3 +19,106 @@ export const resultError = (data: unknown, message: string, code = 500) => {
|
||||
success: false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 路由树递归排序
|
||||
* 1、先给当前层排序
|
||||
* 2、若当前层有children则递归排序
|
||||
* @param {array} tree 根据角色权限过滤后的路由树
|
||||
* @returns 返回排序之后的树
|
||||
*/
|
||||
export const treeSort = (tree: Menu.MenuOptions[]) => {
|
||||
if (!tree || tree.length == 0) return [];
|
||||
tree.sort((a: Menu.MenuOptions, b: Menu.MenuOptions) => {
|
||||
// a和b都是undefined则相等
|
||||
if (a.meta.sort != 0 && !a.meta.sort && b.meta.sort != 0 && !b.meta.sort) {
|
||||
return 0;
|
||||
}
|
||||
// a是undefined则a被排在b之后
|
||||
if (a.meta.sort != 0 && !a.meta.sort) return 1;
|
||||
// b是undefined则b被排在a之后
|
||||
if (b.meta.sort != 0 && !b.meta.sort) return -1;
|
||||
return a.meta.sort - b.meta.sort;
|
||||
});
|
||||
|
||||
// 深层递归
|
||||
return tree.map((item: any) => {
|
||||
if (item?.children?.length > 0) {
|
||||
item.children = treeSort(item.children);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 过滤路由树,返回有权限的树
|
||||
* 1、先过滤停用的菜单,该菜单是不可访问的,直接去掉
|
||||
* 2、根据角色权限过滤原始路由树
|
||||
* @param {array} tree 根据角色权限过滤原始路由树
|
||||
* @returns 返回有权限的树
|
||||
*/
|
||||
export const filterByRole = (tree: any, userRoles: Array<string>) => {
|
||||
return tree.filter((item: any) => {
|
||||
// 过滤角色权限
|
||||
if (item?.meta?.roles) {
|
||||
if (!roleBase(item.meta.roles, userRoles)) return false;
|
||||
}
|
||||
// 过滤是否禁用
|
||||
if (item?.meta?.disable) return false;
|
||||
if (item.children) item.children = filterByRole(item.children, userRoles);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验该角色是否有路由权限
|
||||
* @param {array} roles 路由的角色权限
|
||||
* @returns 是否有权限 true是 false否
|
||||
*/
|
||||
export const roleBase = (roles: Array<string>, userRoles: Array<string>) => {
|
||||
return userRoles.some((item: string) => roles.includes(item));
|
||||
};
|
||||
|
||||
/**
|
||||
* 深拷贝
|
||||
* @param { string } data 需要深拷贝的数据
|
||||
* @returns 深拷贝的数据
|
||||
*/
|
||||
export function deepClone(data: any) {
|
||||
let stack = [];
|
||||
let cloned;
|
||||
if (Array.isArray(data)) {
|
||||
cloned = [];
|
||||
} else if (typeof data === "object" && data !== null) {
|
||||
cloned = {};
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
stack.push({
|
||||
original: data,
|
||||
copy: cloned
|
||||
});
|
||||
while (stack.length > 0) {
|
||||
let current: any = stack.pop();
|
||||
let original = current.original;
|
||||
let copy = current.copy;
|
||||
|
||||
for (let key in original) {
|
||||
if (original.hasOwnProperty(key)) {
|
||||
let value = original[key];
|
||||
|
||||
if (typeof value === "object" && value !== null) {
|
||||
copy[key] = Array.isArray(value) ? [] : {};
|
||||
|
||||
stack.push({
|
||||
original: value,
|
||||
copy: copy[key]
|
||||
});
|
||||
} else {
|
||||
copy[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type { MockMethod } from "vite-plugin-mock";
|
||||
import { resultSuccess, resultError } from "../_utils";
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { deepClone, filterByRole, treeSort, resultSuccess, resultError } from "../_utils";
|
||||
import systemMenu from "../_data/system_menu";
|
||||
|
||||
/**
|
||||
@ -29,67 +30,9 @@ export default [
|
||||
let token = headers.authorization;
|
||||
// 这里模拟两个角色,admin、common
|
||||
let userRoles = token === "Admin-Token" ? ["admin"] : ["common"];
|
||||
systemMenu[0].children = treeSort(filterByRole(systemMenu[0].children, userRoles));
|
||||
return resultSuccess(systemMenu);
|
||||
const originTree: RouteRecordRaw[] = deepClone(systemMenu);
|
||||
originTree[0].children = treeSort(filterByRole(originTree[0].children, userRoles));
|
||||
return resultSuccess(originTree);
|
||||
}
|
||||
}
|
||||
] as MockMethod[];
|
||||
|
||||
/**
|
||||
* 路由树递归排序
|
||||
* 1、先给当前层排序
|
||||
* 2、若当前层有children则递归排序
|
||||
* @param {array} tree 根据角色权限过滤后的路由树
|
||||
* @returns 返回排序之后的树
|
||||
*/
|
||||
export const treeSort = (tree: Menu.MenuOptions[]) => {
|
||||
if (!tree || tree.length == 0) return [];
|
||||
tree.sort((a: Menu.MenuOptions, b: Menu.MenuOptions) => {
|
||||
// a和b都是undefined则相等
|
||||
if (a.meta.sort != 0 && !a.meta.sort && b.meta.sort != 0 && !b.meta.sort) {
|
||||
return 0;
|
||||
}
|
||||
// a是undefined则a被排在b之后
|
||||
if (a.meta.sort != 0 && !a.meta.sort) return 1;
|
||||
// b是undefined则b被排在a之后
|
||||
if (b.meta.sort != 0 && !b.meta.sort) return -1;
|
||||
return a.meta.sort - b.meta.sort;
|
||||
});
|
||||
|
||||
// 深层递归
|
||||
return tree.map((item: any) => {
|
||||
if (item?.children?.length > 0) {
|
||||
item.children = treeSort(item.children);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 过滤路由树,返回有权限的树
|
||||
* 1、先过滤停用的菜单,该菜单是不可访问的,直接去掉
|
||||
* 2、根据角色权限过滤原始路由树
|
||||
* @param {array} tree 根据角色权限过滤原始路由树
|
||||
* @returns 返回有权限的树
|
||||
*/
|
||||
export const filterByRole = (tree: any, userRoles: Array<string>) => {
|
||||
return tree.filter((item: any) => {
|
||||
// 过滤角色权限
|
||||
if (item?.meta?.roles) {
|
||||
if (!roleBase(item.meta.roles, userRoles)) return false;
|
||||
}
|
||||
// 过滤是否禁用
|
||||
if (item?.meta?.disable) return false;
|
||||
if (item.children) item.children = filterByRole(item.children, userRoles);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验该角色是否有路由权限
|
||||
* @param {array} roles 路由的角色权限
|
||||
* @returns 是否有权限 true是 false否
|
||||
*/
|
||||
export const roleBase = (roles: Array<string>, userRoles: Array<string>) => {
|
||||
return userRoles.some((item: string) => roles.includes(item));
|
||||
};
|
||||
|
||||
@ -7,12 +7,12 @@ export default [
|
||||
url: "/mock/login",
|
||||
method: "post",
|
||||
timeout: 300,
|
||||
response: ({ body }) => {
|
||||
response: ({ body }: any) => {
|
||||
let { username, password } = body;
|
||||
if (username === "admin" && password === "123456") {
|
||||
return resultSuccess({ token: "Admin-Token" });
|
||||
}
|
||||
if(username === "common" && password === "123456"){
|
||||
if (username === "common" && password === "123456") {
|
||||
return resultSuccess({ token: "Common-Token" });
|
||||
}
|
||||
return resultError(null, "账号或者密码错误", 500);
|
||||
@ -22,11 +22,21 @@ export default [
|
||||
url: "/mock/user/info",
|
||||
method: "get",
|
||||
timeout: 300,
|
||||
response: () => {
|
||||
let data = {
|
||||
username: "admin", // 用户名
|
||||
roles: ["admin"] // 角色
|
||||
};
|
||||
response: ({ headers }: any) => {
|
||||
let data = {};
|
||||
// 管理员角色
|
||||
if (headers.authorization == "Admin-Token") {
|
||||
data = {
|
||||
username: "admin", // 用户名
|
||||
roles: ["admin"] // 角色
|
||||
};
|
||||
} else {
|
||||
// 普通角色
|
||||
data = {
|
||||
username: "common", // 用户名
|
||||
roles: ["common"] // 角色
|
||||
};
|
||||
}
|
||||
return resultSuccess(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,16 +40,16 @@ const router = createRouter({
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start(); // 开启进度条
|
||||
const store = useUserInfoStore(pinia);
|
||||
const { AdminToken } = storeToRefs(store);
|
||||
const { token } = storeToRefs(store);
|
||||
console.log("去", to, "来自", from);
|
||||
// next()内部加了path等于跳转指定路由会再次触发router.beforeEach,内部无参数等于放行,不会触发router.beforeEach
|
||||
if (to.path === "/login" && !AdminToken.value) {
|
||||
if (to.path === "/login" && !token.value) {
|
||||
// 1、去登录页,无token,放行
|
||||
next();
|
||||
} else if (!AdminToken.value) {
|
||||
} else if (!token.value) {
|
||||
// 2、没有token,直接重定向到登录页
|
||||
next("/login");
|
||||
} else if (to.path === "/login" && AdminToken.value) {
|
||||
} else if (to.path === "/login" && token.value) {
|
||||
// 3、去登录页,有token,直接重定向到home页
|
||||
next("/home");
|
||||
// 项目内的跳转,处理跳转路由高亮
|
||||
|
||||
@ -75,6 +75,12 @@ export const useRoutesConfigStore = defineStore("route-config", {
|
||||
removeRouteNames(list: Array<string>) {
|
||||
this.cacheRoutes = this.cacheRoutes.filter((item: string) => !list.includes(item));
|
||||
},
|
||||
/**
|
||||
* 重置routeTree路由树
|
||||
*/
|
||||
async resetRoute() {
|
||||
this.routeTree = [];
|
||||
},
|
||||
/**
|
||||
* 路由初始化
|
||||
* 1、将模块设置为真实模块
|
||||
|
||||
@ -12,18 +12,18 @@ export const useUserInfoStore = defineStore("user-info", {
|
||||
username: "",
|
||||
roles: []
|
||||
}, // 账号信息
|
||||
AdminToken: "" // token
|
||||
token: "" // token
|
||||
}),
|
||||
actions: {
|
||||
async setAccount(data: Array<string>) {
|
||||
this.account = data;
|
||||
},
|
||||
async setToken(data: string) {
|
||||
this.AdminToken = data;
|
||||
this.token = data;
|
||||
},
|
||||
async logOut() {
|
||||
this.account = {};
|
||||
this.AdminToken = "";
|
||||
this.token = "";
|
||||
}
|
||||
},
|
||||
persist: persistedstateConfig("user-info")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user