refactor: 登陆&获取菜单&登出
This commit is contained in:
parent
9888af431e
commit
7a8b8677bd
|
@ -15,7 +15,7 @@ export default mergeConfig(
|
|||
'/front': {
|
||||
target: 'http://101.43.186.75:8081/',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/front/, ''),
|
||||
rewrite: (path: string) => path.replace(/^\/front/, ''),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
import { Message, Modal } from '@arco-design/web-vue';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useUser from '@/store/modules/user';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import type { ErrorMessageMode } from '#/axios';
|
||||
|
||||
export default function checkStatus(status: number, msg: string, errorMessageMode: ErrorMessageMode = 'message'): void {
|
||||
const { t } = useI18n();
|
||||
const userStore = useUser();
|
||||
let errMessage = '';
|
||||
|
||||
switch (status) {
|
||||
case 400:
|
||||
errMessage = `${msg}`;
|
||||
break;
|
||||
// 401: Not logged in
|
||||
case 401:
|
||||
case 401: {
|
||||
errMessage = msg || t('api.errMsg401');
|
||||
userStore.logout();
|
||||
const router = useRouter();
|
||||
router.push('/login');
|
||||
break;
|
||||
}
|
||||
case 403:
|
||||
errMessage = t('api.errMsg403');
|
||||
break;
|
||||
|
|
|
@ -107,14 +107,12 @@ const transform: AxiosTransform = {
|
|||
/**
|
||||
* @description: 请求拦截器处理
|
||||
*/
|
||||
requestInterceptors: (config, options) => {
|
||||
requestInterceptors: (config) => {
|
||||
// 请求之前处理config
|
||||
const token = getToken();
|
||||
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
||||
// jwt token
|
||||
(config as Recordable).headers.Authorization = options.authenticationScheme
|
||||
? `${options.authenticationScheme} ${token}`
|
||||
: token;
|
||||
const { sessionId, csrfToken } = token;
|
||||
(config as Recordable).headers = { 'X-AUTH-TOKEN': sessionId, 'CSRF-TOKEN': csrfToken };
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
@ -162,8 +160,6 @@ const transform: AxiosTransform = {
|
|||
},
|
||||
};
|
||||
|
||||
const { sessionId, csrfToken } = getToken();
|
||||
|
||||
function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||
return new MSAxios(
|
||||
deepMerge(
|
||||
|
@ -174,7 +170,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
|||
authenticationScheme: '',
|
||||
baseURL: `${window.location.origin}/${import.meta.env.VITE_API_BASE_URL as string}`,
|
||||
timeout: 30 * 1000,
|
||||
headers: { 'Content-Type': ContentTypeEnum.JSON, 'X-AUTH-TOKEN': sessionId, 'CSRF-TOKEN': csrfToken },
|
||||
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
||||
// 如果是form-data格式
|
||||
// headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
|
||||
// 数据处理方式
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import MSR from '@/api/http/index';
|
||||
import { LoginUrl, LogoutUrl, GetUserInfoUrl, GetMenuListUrl } from '@/api/requrls/user';
|
||||
import { LoginUrl, LogoutUrl, GetMenuListUrl, isLoginUrl } from '@/api/requrls/user';
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import type { LoginData, LoginRes } from '@/models/user';
|
||||
import type { UserState } from '@/store/modules/user/types';
|
||||
|
||||
export function login(data: LoginData) {
|
||||
return MSR.post<LoginRes>({ url: LoginUrl, data });
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return MSR.post<LoginRes>({ url: LogoutUrl });
|
||||
export function isLogin() {
|
||||
return MSR.get<LoginRes>({ url: isLoginUrl });
|
||||
}
|
||||
|
||||
export function getUserInfo() {
|
||||
return MSR.post<UserState>({ url: GetUserInfoUrl });
|
||||
export function logout() {
|
||||
return MSR.get<LoginRes>({ url: LogoutUrl });
|
||||
}
|
||||
|
||||
export function getMenuList() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export const LoginUrl = '/login';
|
||||
export const LogoutUrl = '/api/user/logout';
|
||||
export const GetUserInfoUrl = '/api/user/info';
|
||||
export const isLoginUrl = '/is-login';
|
||||
export const LogoutUrl = '/signout';
|
||||
export const GetMenuListUrl = '/api/user/menu';
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const { logout } = useUser();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { logout } = useUser();
|
||||
const { menuTree } = useMenuTree();
|
||||
const collapsed = computed({
|
||||
get() {
|
||||
|
@ -121,7 +121,6 @@
|
|||
{
|
||||
label: t('personal.switchOrg'),
|
||||
icon: <icon-swap />,
|
||||
event: () => {},
|
||||
},
|
||||
{
|
||||
divider: <a-divider class="ms-dropdown-divider" />,
|
||||
|
@ -129,7 +128,7 @@
|
|||
{
|
||||
label: t('personal.exit'),
|
||||
icon: <icon-export />,
|
||||
event: logout,
|
||||
event: () => logout(),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -154,6 +154,7 @@ export default function useTableProps(
|
|||
setPagination({ current: data.current, total: data.total });
|
||||
return data;
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err);
|
||||
// TODO 表格异常放到solt的empty
|
||||
} finally {
|
||||
|
|
|
@ -11,17 +11,15 @@ export default function useUser() {
|
|||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
const logout = async (logoutTo?: string) => {
|
||||
/**
|
||||
* 登出
|
||||
* @param logoutTo 登出后跳转的页面
|
||||
* @returns
|
||||
*/
|
||||
const logout = async () => {
|
||||
await userStore.logout();
|
||||
const currentRoute = router.currentRoute.value;
|
||||
Message.success(t('message.logoutSuccess'));
|
||||
router.push({
|
||||
name: logoutTo && typeof logoutTo === 'string' ? logoutTo : 'login',
|
||||
query: {
|
||||
...router.currentRoute.value.query,
|
||||
redirect: currentRoute.name as string,
|
||||
},
|
||||
});
|
||||
router.push({ name: 'login' });
|
||||
};
|
||||
return {
|
||||
logout,
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import Mock from 'mockjs';
|
||||
import setupMock, { successResponseWrap, failResponseWrap, makeMockUrl } from '@/utils/setup-mock';
|
||||
|
||||
import { GetMenuListUrl, LogoutUrl, GetUserInfoUrl, LoginUrl } from '@/api/requrls/user';
|
||||
import { GetMenuListUrl, LogoutUrl, LoginUrl } from '@/api/requrls/user';
|
||||
import { isLogin } from '@/utils/auth';
|
||||
|
||||
setupMock({
|
||||
mock: false,
|
||||
setup() {
|
||||
// 用户信息
|
||||
Mock.mock(makeMockUrl(GetUserInfoUrl), () => {
|
||||
if (isLogin()) {
|
||||
Mock.mock(makeMockUrl('/api/userinfo'), async () => {
|
||||
if (await isLogin()) {
|
||||
const role = window.localStorage.getItem('userRole') || 'admin';
|
||||
return successResponseWrap({
|
||||
name: '王立群',
|
||||
|
@ -32,7 +33,7 @@ setupMock({
|
|||
return failResponseWrap(null, '未登录', 50008);
|
||||
});
|
||||
|
||||
// 登出
|
||||
// 登陆
|
||||
Mock.mock(makeMockUrl(LoginUrl), () => {
|
||||
return successResponseWrap({});
|
||||
});
|
||||
|
|
|
@ -5,8 +5,35 @@ export interface LoginData {
|
|||
authenticate: string;
|
||||
}
|
||||
|
||||
export interface UserRole {
|
||||
id: string;
|
||||
createTime: number;
|
||||
createUser: string;
|
||||
roleId: string;
|
||||
sourceId: string;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
// 登录返回
|
||||
export interface LoginRes {
|
||||
sessionId: string;
|
||||
csrfToken: string;
|
||||
createTime: number;
|
||||
createUser: string;
|
||||
email: string;
|
||||
enabled: boolean;
|
||||
id: string;
|
||||
language: string;
|
||||
lastOrganizationId: string;
|
||||
lastProjectId: string;
|
||||
name: string;
|
||||
phone: string;
|
||||
platformInfo: string;
|
||||
seleniumServer: string;
|
||||
sessionId: string;
|
||||
source: string;
|
||||
updateTime: number;
|
||||
updateUser: string;
|
||||
userRolePermissions: UserRole[];
|
||||
userRoleRelations: UserRole[];
|
||||
userRoles: UserRole[];
|
||||
}
|
||||
|
|
|
@ -1,31 +1,17 @@
|
|||
import type { Router, RouteRecordNormalized } from 'vue-router';
|
||||
import type { Router } from 'vue-router';
|
||||
import NProgress from 'nprogress'; // progress bar
|
||||
|
||||
import usePermission from '@/hooks/usePermission';
|
||||
import { useAppStore } from '@/store';
|
||||
import { WHITE_LIST, NOT_FOUND } from '../constants';
|
||||
|
||||
export default function setupPermissionGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const appStore = useAppStore();
|
||||
const Permission = usePermission();
|
||||
const permissionsAllow = Permission.accessRouter(to);
|
||||
// 针对来自服务端的菜单配置进行处理
|
||||
if (!appStore.appAsyncMenus.length && !WHITE_LIST.find((el) => el.name === to.name)) {
|
||||
await appStore.fetchServerMenuConfig();
|
||||
}
|
||||
const serverMenuConfig = [...appStore.appAsyncMenus, ...WHITE_LIST];
|
||||
|
||||
let exist = false;
|
||||
while (serverMenuConfig.length && !exist) {
|
||||
const element = serverMenuConfig.shift();
|
||||
if (element?.name === to.name) exist = true;
|
||||
const exist = WHITE_LIST.find((el) => el.name === to.name);
|
||||
|
||||
if (element?.children) {
|
||||
serverMenuConfig.push(...(element.children as unknown as RouteRecordNormalized[]));
|
||||
}
|
||||
}
|
||||
if (exist && permissionsAllow) {
|
||||
if (exist || permissionsAllow) {
|
||||
next();
|
||||
} else next(NOT_FOUND);
|
||||
NProgress.done();
|
||||
|
|
|
@ -1,32 +1,12 @@
|
|||
import type { Router, LocationQueryRaw } from 'vue-router';
|
||||
import NProgress from 'nprogress'; // progress bar
|
||||
|
||||
import { useUserStore } from '@/store';
|
||||
import { isLogin } from '@/utils/auth';
|
||||
|
||||
export default function setupUserLoginInfoGuard(router: Router) {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start();
|
||||
const userStore = useUserStore();
|
||||
if (isLogin()) {
|
||||
if (userStore.role) {
|
||||
if (to.name !== 'login' && (await isLogin())) {
|
||||
next();
|
||||
} else {
|
||||
try {
|
||||
await userStore.info();
|
||||
next();
|
||||
} catch (error) {
|
||||
// 获取用户信息错误则先退出,重定向到登录页
|
||||
await userStore.logout();
|
||||
next({
|
||||
name: 'login',
|
||||
query: {
|
||||
redirect: to.name,
|
||||
...to.query,
|
||||
} as LocationQueryRaw,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 未登录的都直接跳转至登录页,访问的页面地址缓存到 query 上
|
||||
if (to.name === 'login') {
|
||||
|
|
|
@ -4,6 +4,7 @@ import { AppRouteRecordRaw } from '../types';
|
|||
const ApiTest: AppRouteRecordRaw = {
|
||||
path: '/api-test',
|
||||
name: 'apiTest',
|
||||
redirect: '/api-test/list',
|
||||
component: DEFAULT_LAYOUT,
|
||||
meta: {
|
||||
locale: 'menu.apiTest',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { login as userLogin, logout as userLogout, getUserInfo } from '@/api/modules/user';
|
||||
import { login as userLogin, logout as userLogout } from '@/api/modules/user';
|
||||
import { setToken, clearToken } from '@/utils/auth';
|
||||
import { removeRouteListener } from '@/utils/route-listener';
|
||||
import useAppStore from '../app';
|
||||
|
@ -51,22 +51,17 @@ const useUserStore = defineStore('user', {
|
|||
this.$reset();
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
async info() {
|
||||
const res = await getUserInfo();
|
||||
const appStore = useAppStore();
|
||||
if (appStore.currentOrgId === '') {
|
||||
// 第一次进系统才设置组织 ID,后续已经持久化存储了
|
||||
appStore.setCurrentOrgId(res.organization || '');
|
||||
}
|
||||
this.setInfo(res);
|
||||
},
|
||||
|
||||
// 登录
|
||||
async login(loginForm: LoginData) {
|
||||
try {
|
||||
const res = await userLogin(loginForm);
|
||||
setToken(res.sessionId, res.csrfToken);
|
||||
const appStore = useAppStore();
|
||||
if (appStore.currentOrgId === '') {
|
||||
// 第一次进系统才设置组织 ID,后续已经持久化存储了
|
||||
appStore.setCurrentOrgId(res.lastOrganizationId || '');
|
||||
}
|
||||
this.setInfo(res);
|
||||
} catch (err) {
|
||||
clearToken();
|
||||
throw err;
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import { isLogin as isLoginFun } from '@/api/modules/user';
|
||||
|
||||
const SESSION_ID = 'sessionId';
|
||||
const CSRF_TOKEN = 'csrfToken';
|
||||
|
||||
const isLogin = () => {
|
||||
return !!localStorage.getItem(SESSION_ID);
|
||||
const isLogin = async () => {
|
||||
try {
|
||||
await isLoginFun();
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// 获取token
|
||||
const getToken = () => {
|
||||
|
|
|
@ -101,13 +101,6 @@
|
|||
setLoading(true);
|
||||
try {
|
||||
await userStore.login(values as LoginData);
|
||||
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
||||
router.push({
|
||||
name: (redirect as string) || 'apiTest',
|
||||
query: {
|
||||
...othersQuery,
|
||||
},
|
||||
});
|
||||
Message.success(t('login.form.login.success'));
|
||||
const { rememberPassword } = loginConfig.value;
|
||||
const { username, password } = values;
|
||||
|
@ -115,6 +108,13 @@
|
|||
// The actual production environment requires encrypted storage.
|
||||
loginConfig.value.username = rememberPassword ? username : '';
|
||||
loginConfig.value.password = rememberPassword ? password : '';
|
||||
const { redirect, ...othersQuery } = router.currentRoute.value.query;
|
||||
router.push({
|
||||
name: (redirect as string) || 'apiTest',
|
||||
query: {
|
||||
...othersQuery,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
errorMessage.value = (err as Error).message;
|
||||
} finally {
|
||||
|
|
Loading…
Reference in New Issue