From d855c8f8fb8edc48b6c55f08ee2ebf74415cc5ea Mon Sep 17 00:00:00 2001 From: nanxun Date: Tue, 13 Jan 2026 22:07:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=EF=BC=9A=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=97=A0=E6=84=9F=E5=88=B7=E6=96=B0=20=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=88=B7=E6=96=B0token=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=BC=82=E5=B8=B8=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/RedisRefreshTokenService.cs | 4 +- frontend/web/src/services/api.js | 49 +++++++++++++++++-- frontend/web/src/stores/auth.js | 12 +++-- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/backend/IM_API/Services/RedisRefreshTokenService.cs b/backend/IM_API/Services/RedisRefreshTokenService.cs index 260041d..60b3a81 100644 --- a/backend/IM_API/Services/RedisRefreshTokenService.cs +++ b/backend/IM_API/Services/RedisRefreshTokenService.cs @@ -53,8 +53,8 @@ namespace IM_API.Services if (json.IsNullOrEmpty) return (false,-1); try { - var doc = JsonConvert.DeserializeObject(json); - var userId = doc.GetProperty("UserId").GetInt32(); + using var doc = JsonDocument.Parse(json.ToString()); + var userId = doc.RootElement.GetProperty("UserId").GetInt32(); return (true,userId); } catch diff --git a/frontend/web/src/services/api.js b/frontend/web/src/services/api.js index e0040ca..3243dcd 100644 --- a/frontend/web/src/services/api.js +++ b/frontend/web/src/services/api.js @@ -2,10 +2,15 @@ import axios from 'axios' import { useMessage } from '@/components/messages/useAlert'; import router from '@/router'; import { useAuthStore } from '@/stores/auth'; +import { authService } from './auth'; const message = useMessage(); const authStore = useAuthStore(); +let waitqueue = []; +let isRefreshing = false; +const authURL = ['/auth/login', '/auth/register', '/auth/refresh']; + const api = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api', // 从环境变量中读取基础 URL timeout: 10000, @@ -31,16 +36,50 @@ api.interceptors.response.use( response => { return response.data; }, - err => { - if (err.response) { - switch (err.response.status) { + async err => { + const { config, response } = err; + if (response) { + switch (response.status) { case 401: + if (authURL.some(x => config.url.includes(x))) { + authStore.logout(); + message.error('未登录,请登录后操作。'); + router.push('/auth/login') + break; + } + if (config._retry) { + break; + } + + config._retry = true; + // 已经在刷新 → 排队 + if (isRefreshing) { + return new Promise(resolve => { + waitqueue.push(token => { + config.headers.Authorization = `Bearer ${token}` + resolve(api(config)) + }) + }) + } + + isRefreshing = true; + const refreshToken = authStore.refreshToken; + console.log(authStore) + if (refreshToken != null && refreshToken != '') { + const res = await authService.refresh(refreshToken) + authStore.setLoginInfo(res.data.token, res.data.refreshToken, res.data.userInfo) + waitqueue.forEach(cb => cb(authStore.token)); + waitqueue = []; + config.headers.Authorization = `Bearer ${authStore.token}` + return api(config) + } + authStore.logout(); message.error('未登录,请登录后操作。'); router.push('/auth/login') break; case 400: - if (err.response.data && err.response.data.code == 1003) { - message.error(err.response.data.message); + if (response.data && response.data.code == 1003) { + message.error(response.data.message); break; } default: diff --git a/frontend/web/src/stores/auth.js b/frontend/web/src/stores/auth.js index 2193a06..edde2ab 100644 --- a/frontend/web/src/stores/auth.js +++ b/frontend/web/src/stores/auth.js @@ -4,10 +4,10 @@ import { defineStore } from 'pinia' export const useAuthStore = defineStore('auth', () => { const token = ref(localStorage.getItem('user_token') || ''); const refreshToken = ref(localStorage.getItem('refresh_token') || ''); - const userInfo = ref(localStorage.getItem('user_info') || ''); + const userInfo = ref(JSON.parse(localStorage.getItem('user_info')) || {}); //判断是否已登录 - const isLoggedIn = computed(() => !!token.value); + const isLoggedIn = computed(() => !!refreshToken.value); /** * 登录成功保存状态 @@ -15,12 +15,13 @@ export const useAuthStore = defineStore('auth', () => { * @param {*} user 用户信息 */ function setLoginInfo(newToken, newRefreshToken, user) { + console.log(`设置凭证:\ntoken:${newToken}\nrefreshToken:${newRefreshToken}`) token.value = newToken; refreshToken.value = newRefreshToken userInfo.value = user; localStorage.setItem('user_token', newToken); - localStorage.setItem('refresh_token', refreshToken) - localStorage.setItem('user_info', user) + localStorage.setItem('refresh_token', newRefreshToken) + localStorage.setItem('user_info', JSON.stringify(user)) } /** @@ -29,10 +30,11 @@ export const useAuthStore = defineStore('auth', () => { function logout() { token.value = ''; userInfo.value = null; + refreshToken.value = '' localStorage.removeItem('user_token'); localStorage.removeItem('refresh_token') localStorage.removeItem('user_info') } - return { token, userInfo, isLoggedIn, setLoginInfo, logout }; + return { token, refreshToken, userInfo, isLoggedIn, setLoginInfo, logout }; })