刷新多此会跳转到登陆的问题解决了,但是刷新会出现更逆天的问题

This commit is contained in:
Kunagisa 2025-06-14 22:08:47 +08:00
parent f905a984f2
commit c6c6df9c57
5 changed files with 110 additions and 85 deletions

View File

@ -13,48 +13,49 @@ const axiosInstance = axios.create({
timeout: 10000 timeout: 10000
}); });
/** export function setupInterceptors() {
* 请求拦截器 /**
* - 对需要认证的请求在请求头中添加Authorization * 请求拦截器
* - 对登录注册和获取列表的请求不添加Authorization * - 对需要认证的请求在请求头中添加Authorization
*/ * - 对登录注册和获取列表的请求不添加Authorization
axiosInstance.interceptors.request.use( */
config => { axiosInstance.interceptors.request.use(
const token = localStorage.getItem('access_token'); config => {
const url = config.url; const token = localStorage.getItem('access_token');
const url = config.url;
// 定义不需要Token的接口条件 // 定义不需要Token的接口条件
const noAuthRequired = const noAuthRequired =
url === '/user/login' || url === '/user/login' ||
url === '/user/register' || // 明确添加注册接口 url === '/user/register';
url.endsWith('/getlist');
if (token && !noAuthRequired) { if (token && !noAuthRequired) {
config.headers.Authorization = `Bearer ${token}`; config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
} }
return config; );
},
error => {
return Promise.reject(error);
}
);
/** /**
* 响应拦截器 * 响应拦截器
* - 如果收到401错误未授权并且不是来自登录请求则调用logoutUser函数清除用户凭证 * - 如果收到401错误未授权并且不是来自登录请求则调用logoutUser函数清除用户凭证
*/ */
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(
response => response, response => response,
error => { error => {
const originalRequest = error.config; const originalRequest = error.config;
// 如果收到401错误并且不是来自登录请求本身 // 如果收到401错误并且不是来自登录请求本身
if (error.response && error.response.status === 401 && originalRequest.url !== '/user/login') { if (error.response && error.response.status === 401 && originalRequest.url !== '/user/login') {
logoutUser(); // 调用简化的logoutUser它只清除token不导航 logoutUser(); // 调用简化的logoutUser它只清除token不导航
}
// 不需要额外的console.error错误会自然地在调用处被捕获或显示在网络请求中
return Promise.reject(error);
} }
// 不需要额外的console.error错误会自然地在调用处被捕获或显示在网络请求中 );
return Promise.reject(error); }
}
);
export default axiosInstance; export default axiosInstance;

View File

@ -112,9 +112,6 @@ const refreshCaptcha = async () => {
} }
} }
onMounted(() => {
refreshCaptcha()
})
const handleLogin = async () => { const handleLogin = async () => {
try { try {
@ -203,6 +200,11 @@ const validateForm = () => {
return true return true
} }
onMounted(() => {
refreshCaptcha()
})
</script> </script>
<style scoped> <style scoped>

View File

@ -2,7 +2,13 @@ import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import './assets/styles/common.css' import './assets/styles/common.css'
import { setupInterceptors } from './api/axiosConfig'; // 导入拦截器设置函数
// 在应用初始化最开始就设置好拦截器
setupInterceptors();
const app = createApp(App) const app = createApp(App)
app.use(router) app.use(router)
app.mount('#app') app.mount('#app')

View File

@ -1,5 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import { hasValidToken, getUserInfo, logoutUser } from '../utils/jwt'; import { hasValidToken, getUserInfo, logoutUser, getStoredUser } from '../utils/jwt';
import { justLoggedIn } from '../utils/authSessionState'; import { justLoggedIn } from '../utils/authSessionState';
import EditorsMaps from '@/views/index/EditorsMaps.vue' import EditorsMaps from '@/views/index/EditorsMaps.vue'
@ -113,40 +113,44 @@ const router = createRouter({
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth); const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
if (requiresAuth) { if (!requiresAuth) {
const tokenExists = hasValidToken(); return next(); // 如果页面不需要认证,直接放行
}
if (justLoggedIn.value) { // 方案1: 检查SessionStorage中是否已有用户信息 (刷新后最先检查这里)
justLoggedIn.value = false; if (getStoredUser()) {
if (tokenExists) { return next();
next(); }
} else {
logoutUser();
next({ path: '/backend/login', query: { redirect: to.fullPath, sessionExpired: 'true' }});
}
return;
}
if (tokenExists) { // 方案2: 如果刚登录过,直接放行
const user = await getUserInfo(); if (justLoggedIn.value) {
if (user) { justLoggedIn.value = false;
next(); // 此时token肯定有效但可能还没来得及请求用户信息
} else { // 为确保用户信息被存储,可以调用一次,但不阻塞导航
logoutUser(); getUserInfo().catch(err => console.error("Error fetching user info after login:", err));
next({ return next();
path: '/backend/login', }
query: { redirect: to.fullPath, sessionExpired: 'true' }
}); // 方案3: 检查localStorage中是否有token (刷新后SessionStorage没有用户信息时)
} if (hasValidToken()) {
} else { try {
next({ await getUserInfo(); // 尝试获取用户信息该函数会把用户信息存入SessionStorage
return next(); // 获取成功,放行
} catch (error) {
// 获取失败 (token无效, 网络问题等)
logoutUser(); // 清除所有凭证
return next({
path: '/backend/login', path: '/backend/login',
query: { redirect: to.fullPath } query: { redirect: to.fullPath, sessionExpired: 'true' }
}); });
} }
} else {
next();
} }
// 方案4: 如果以上条件都不满足,跳转登录页
return next({
path: '/backend/login',
query: { redirect: to.fullPath }
});
}); });
export default router export default router

View File

@ -4,6 +4,23 @@ import { justLoggedIn } from './authSessionState'; // Import the flag
const USER_INFO_URL = '/user'; // 获取用户信息的API端点 const USER_INFO_URL = '/user'; // 获取用户信息的API端点
/**
* sessionStorage 中安全地读取和解析用户信息
* @returns {Object|null} 用户信息对象或null
*/
export const getStoredUser = () => {
const storedUser = sessionStorage.getItem('currentUser');
if (storedUser) {
try {
return JSON.parse(storedUser);
} catch (e) {
console.error('Error parsing stored user info:', e);
return null;
}
}
return null;
};
/** /**
* 检查Token是否存在且理论上是否在有效期内 * 检查Token是否存在且理论上是否在有效期内
* 服务端 /user接口会验证实际有效期 * 服务端 /user接口会验证实际有效期
@ -22,24 +39,18 @@ export const hasValidToken = () => {
*/ */
export const getUserInfo = async () => { export const getUserInfo = async () => {
if (!hasValidToken()) { if (!hasValidToken()) {
console.log(' No token found, skipping getUserInfo.'); throw new Error('No valid token found');
return null;
} }
try { try {
// console.log('jwt.js: Attempting to fetch user info from', USER_INFO_URL);
const response = await axiosInstance.get(USER_INFO_URL); const response = await axiosInstance.get(USER_INFO_URL);
if (response.status !== 200) { const user = response.data;
router.push('/backend/login'); // 将获取到的用户信息存入 sessionStorage
return null; sessionStorage.setItem('currentUser', JSON.stringify(user));
} return user;
// console.log('jwt.js: User info received:', response.data);
return response.data;
} catch (error) { } catch (error) {
router.push('/backend/login'); // 清除可能存在的无效用户信息
// console.error('jwt.js: Error fetching user info:', error.response ? error.response.status : error.message); sessionStorage.removeItem('currentUser');
// 401错误会被响应拦截器处理清除token然后错误会传播到这里 throw error;
// 其他网络错误等也会被捕获
return null;
} }
}; };
@ -51,6 +62,7 @@ export const logoutUser = () => { // 不再是 async因为它不执行异步
// console.log('jwt.js: logoutUser called. Clearing local storage.'); // console.log('jwt.js: logoutUser called. Clearing local storage.');
localStorage.removeItem('access_token'); localStorage.removeItem('access_token');
localStorage.removeItem('user_id'); localStorage.removeItem('user_id');
sessionStorage.removeItem('currentUser'); // 同时清除sessionStorage中的用户信息
// 导航将由调用者(如路由守卫)处理 // 导航将由调用者(如路由守卫)处理
}; };