feat: 优化
This commit is contained in:
@@ -24,8 +24,7 @@ const WHITE_LIST = [
|
||||
* 检查 URL 是否在白名单中
|
||||
*/
|
||||
function isInWhiteList(url) {
|
||||
if (!url) return false
|
||||
return WHITE_LIST.some((path) => url.includes(path))
|
||||
return !!(url && WHITE_LIST.some(path => url.includes(path)))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,23 +58,34 @@ function onRefreshed() {
|
||||
* 注意:只做清理工作,不处理重定向(重定向由上层回调处理)
|
||||
*/
|
||||
let isHandling401 = false
|
||||
let handling401Timeout = null
|
||||
|
||||
function handle401Error(error) {
|
||||
if (isHandling401) return
|
||||
|
||||
isHandling401 = true
|
||||
|
||||
try {
|
||||
// 清空token
|
||||
tokenManager.clearTokens()
|
||||
console.warn('Token已清空,因401错误')
|
||||
} catch (e) {
|
||||
console.error('清空 token 失败:', e)
|
||||
// 防止重复处理
|
||||
if (isHandling401 || error?._handled) {
|
||||
return
|
||||
}
|
||||
|
||||
// 延迟重置标志
|
||||
setTimeout(() => {
|
||||
isHandling401 = true
|
||||
error && (error._handled = true)
|
||||
|
||||
try {
|
||||
tokenManager.clearTokens()
|
||||
const message = error?.needRelogin
|
||||
? '[HTTP] Token已清空,refreshToken无效需要重新登录'
|
||||
: '[HTTP] Token已清空,因401错误'
|
||||
console.warn(message)
|
||||
} catch (e) {
|
||||
console.error('[HTTP] 清空 token 失败:', e)
|
||||
}
|
||||
|
||||
if (handling401Timeout) {
|
||||
clearTimeout(handling401Timeout)
|
||||
}
|
||||
|
||||
handling401Timeout = setTimeout(() => {
|
||||
isHandling401 = false
|
||||
}, 2000)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,66 +127,71 @@ export function createClientAxios(options = {}) {
|
||||
// 检查是否需要认证
|
||||
const needToken = config.headers?.isToken !== false && !isInWhiteList(config.url || '')
|
||||
|
||||
if (needToken) {
|
||||
// 检查 token 是否即将过期(30秒缓冲)
|
||||
const BUFFER_TIME = 30 * 1000
|
||||
const currentToken = tokenManager.getAccessToken()
|
||||
if (!needToken) {
|
||||
return config
|
||||
}
|
||||
|
||||
if (!currentToken) {
|
||||
console.warn('[Token] 没有可用的 accessToken')
|
||||
// 检查 token 是否即将过期(30秒缓冲)
|
||||
const BUFFER_TIME = 30 * 1000
|
||||
const currentToken = tokenManager.getAccessToken()
|
||||
|
||||
if (!currentToken) {
|
||||
console.warn('[Token] 没有可用的 accessToken')
|
||||
return config
|
||||
}
|
||||
|
||||
const isTokenExpired = tokenManager.isExpired(BUFFER_TIME)
|
||||
|
||||
if (!isTokenExpired) {
|
||||
// Token 未过期,直接添加 Authorization 头
|
||||
const authHeader = tokenManager.getAuthHeader()
|
||||
if (authHeader) {
|
||||
config.headers.Authorization = authHeader
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// Token 即将过期,需要刷新
|
||||
console.info('[Token] Token刷新')
|
||||
|
||||
// 如果不在刷新过程中,启动刷新
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true
|
||||
|
||||
if (!refreshTokenFn || typeof refreshTokenFn !== 'function') {
|
||||
console.warn('[Token] 未提供刷新函数,跳过刷新')
|
||||
isRefreshing = false
|
||||
onRefreshed()
|
||||
return config
|
||||
}
|
||||
|
||||
const isTokenExpired = tokenManager.isExpired(BUFFER_TIME)
|
||||
|
||||
if (isTokenExpired) {
|
||||
console.info('[Token] Token刷新')
|
||||
|
||||
// 如果不在刷新过程中,启动刷新
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true
|
||||
|
||||
// 执行刷新(使用上层传入的刷新函数)
|
||||
if (refreshTokenFn && typeof refreshTokenFn === 'function') {
|
||||
refreshTokenFn()
|
||||
.then(() => {
|
||||
console.info('[Token] 刷新成功')
|
||||
isRefreshing = false
|
||||
onRefreshed()
|
||||
})
|
||||
.catch((error) => {
|
||||
isRefreshing = false
|
||||
onRefreshed()
|
||||
console.error('[Token] 刷新失败:', error.message)
|
||||
})
|
||||
} else {
|
||||
console.warn('[Token] 未提供刷新函数,跳过刷新')
|
||||
isRefreshing = false
|
||||
onRefreshed()
|
||||
}
|
||||
}
|
||||
|
||||
// 等待刷新完成
|
||||
return new Promise((resolve) => {
|
||||
subscribeTokenRefresh(() => {
|
||||
// 刷新完成后,重新获取 token 并添加到请求头
|
||||
const authHeader = tokenManager.getAuthHeader()
|
||||
if (authHeader) {
|
||||
config.headers.Authorization = authHeader
|
||||
}
|
||||
resolve(config)
|
||||
})
|
||||
refreshTokenFn()
|
||||
.then(() => {
|
||||
console.info('[Token] 刷新成功')
|
||||
isRefreshing = false
|
||||
onRefreshed()
|
||||
})
|
||||
} else {
|
||||
// Token 未过期,直接添加 Authorization 头
|
||||
.catch((error) => {
|
||||
isRefreshing = false
|
||||
onRefreshed()
|
||||
console.error('[Token] 刷新失败:', error.message)
|
||||
|
||||
if (error.code === 401 && error.needRelogin) {
|
||||
console.info('[Token] refreshToken无效,需要重新登录')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 等待刷新完成
|
||||
return new Promise((resolve) => {
|
||||
subscribeTokenRefresh(() => {
|
||||
const authHeader = tokenManager.getAuthHeader()
|
||||
if (authHeader) {
|
||||
config.headers.Authorization = authHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
resolve(config)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// 响应拦截器
|
||||
@@ -195,31 +210,24 @@ export function createClientAxios(options = {}) {
|
||||
error.code = data?.code
|
||||
error.data = data
|
||||
|
||||
// 业务码 401/403 只在响应拦截器处理,避免重复处理
|
||||
if (data.code === 401 && typeof on401 === 'function') {
|
||||
on401(error)
|
||||
}
|
||||
|
||||
// 业务码 403 只在响应拦截器处理,避免重复处理
|
||||
if (data.code === 403 && typeof on403 === 'function') {
|
||||
// 使用通用的权限不足错误,不使用后端返回的 message
|
||||
const forbiddenError = new Error('权限不足,无法访问该资源')
|
||||
forbiddenError.code = 403
|
||||
on403(forbiddenError)
|
||||
}
|
||||
|
||||
// 抛出错误,业务代码可以捕获
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
return data
|
||||
},
|
||||
(error) => {
|
||||
// HTTP 状态码 401/403 只在错误拦截器处理
|
||||
if (error.response?.status === 401 && typeof on401 === 'function') {
|
||||
on401(error)
|
||||
}
|
||||
const status = error.response?.status
|
||||
|
||||
if (error.response?.status === 403 && typeof on403 === 'function') {
|
||||
if (status === 401 && typeof on401 === 'function') {
|
||||
on401(error)
|
||||
} else if (status === 403 && typeof on403 === 'function') {
|
||||
const forbiddenError = new Error('权限不足,无法访问该资源')
|
||||
forbiddenError.code = 403
|
||||
on403(forbiddenError)
|
||||
|
||||
Reference in New Issue
Block a user