滑动续期与自动刷新:

Web:访问令牌 10 分钟;活跃时自动轮换刷新令牌;连续无操作 30 分钟后清除登录并跳转登录页。
App:会话窗口 3 天;打开 App、恢复前台或请求接口时自动刷新并重新顺延 3 天。
普通登录和 SSO 使用同一策略。
刷新令牌只以 SHA-256 摘要入库,每次刷新都会轮换,旧令牌无法再次使用;退出登录会吊销刷新令牌。
网络临时故障不会误清登录状态,多标签页同时刷新也做了竞争处理。
This commit is contained in:
2026-08-03 19:20:39 +08:00 Unverified
parent 6bee29a351
commit 5ec62a03ca
24 changed files with 6986 additions and 75 deletions
+34 -5
View File
@@ -1,27 +1,56 @@
import axios from 'axios'
import { goLogin } from '../utils/navigate'
import {
authStorageKeys,
clearAuthSession,
markActivity,
refreshAuthSession,
refreshIfNeeded,
} from '../auth/session'
const http = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL ?? '/api',
timeout: 15000,
})
http.interceptors.request.use((config) => {
const token = localStorage.getItem('jiaowu_token')
http.interceptors.request.use(async (config) => {
const isAuthenticationRequest =
config.url?.endsWith('/auth/login') ||
config.url?.endsWith('/auth/refresh') ||
config.url?.endsWith('/auth/logout') ||
config.url?.endsWith('/auth/sso/exchange') ||
config.url?.endsWith('/auth/sso/bind')
if (!isAuthenticationRequest) {
const activeToken = await refreshIfNeeded(true)
if (activeToken) markActivity()
}
const token = localStorage.getItem(authStorageKeys.token)
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
http.interceptors.response.use(
(response) => response,
(error) => {
async (error) => {
const isAuthenticationRequest =
error.config?.url?.endsWith('/auth/login') ||
error.config?.url?.endsWith('/auth/refresh') ||
error.config?.url?.endsWith('/auth/logout') ||
error.config?.url?.endsWith('/auth/sso/exchange') ||
error.config?.url?.endsWith('/auth/sso/bind')
const retryableConfig = error.config as
(typeof error.config & { _jiaowuRetried?: boolean }) | undefined
if (error.response?.status === 401 && !isAuthenticationRequest &&
!retryableConfig?._jiaowuRetried) {
const token = await refreshAuthSession()
if (token && retryableConfig) {
retryableConfig._jiaowuRetried = true
retryableConfig.headers.Authorization = `Bearer ${token}`
return http.request(retryableConfig)
}
}
if (error.response?.status === 401 && !isAuthenticationRequest) {
localStorage.removeItem('jiaowu_token')
localStorage.removeItem('jiaowu_user')
clearAuthSession()
goLogin(location.pathname + location.search + location.hash)
}
return Promise.reject(error)