feat: 前端 401 拦截器 — access token 过期时自动刷新并重试原请求
api.ts: - 增加 setAuthCallbacks 回调注入机制(避免 api/auth 循环依赖) - request() 对非公开路径自动附加 Authorization header - 收到 401 时自动触发 refresh token 刷新,成功后重试原请求 - 并发保护:多个 401 只触发一次 refresh,其余等待同一 Promise - refreshTokenDirect 内部方法绕过 401 拦截避免递归 auth.tsx: - 用 ref 保存 persistAuth/scheduleRefresh 最新引用(避免闭包陈旧) - 初始化时调用 setAuthCallbacks 注入认证回调
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
} from "react";
|
||||
import * as api from "./api";
|
||||
import type { AuthUser } from "./api";
|
||||
import { setAuthCallbacks } from "./api";
|
||||
import {
|
||||
clearAuth,
|
||||
loadAccessToken,
|
||||
@@ -108,6 +109,29 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
[clearRefreshTimer, persistAuth]
|
||||
);
|
||||
|
||||
// 用 ref 保存最新回调,供 API 层 401 拦截器使用(避免闭包陈旧)
|
||||
const persistAuthRef = useRef(persistAuth);
|
||||
const scheduleRefreshRef = useRef(scheduleRefresh);
|
||||
persistAuthRef.current = persistAuth;
|
||||
scheduleRefreshRef.current = scheduleRefresh;
|
||||
|
||||
// 注册 API 层认证回调(用于 401 拦截器)
|
||||
useEffect(() => {
|
||||
setAuthCallbacks({
|
||||
getAccessToken: () => loadAccessToken(),
|
||||
getRefreshToken: () => loadRefreshToken(),
|
||||
onRefreshSuccess: (u, at, rt) => {
|
||||
persistAuthRef.current(u, at, rt);
|
||||
scheduleRefreshRef.current(at);
|
||||
},
|
||||
onRefreshFailed: () => {
|
||||
clearAuth();
|
||||
setUser(null);
|
||||
setAccessToken(null);
|
||||
},
|
||||
});
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// 初始化:检查已有 token 并尝试刷新
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
|
||||
Reference in New Issue
Block a user