You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Spring Boot项目中Axios偶发401错误原因排查

可能的原因及修复方案

1. 异步token设置与组件渲染的竞态问题

你的AuthProvider在渲染时会异步调用SignInPostService设置Axios全局token,但这个异步操作完成前(isLoading仍为true),代码没有明确返回内容,导致React可能提前渲染子组件。此时子组件发起的API请求会因token未设置返回401,刷新页面后SignInPostService已完成,token正常设置,请求恢复正常。

修复方案:
修改AuthProvider逻辑,在isLoading为true时返回加载状态阻止子组件提前渲染,同时用useEffect处理异步逻辑(避免在渲染阶段执行副作用):

function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<any>(window.localStorage.getItem('userFromStorage'));
  const [isLoading, setIsLoading] = useState(true);

  let signin = (newUserMail: string, callback: VoidFunction, newUserObject: Object) => {
    return fakeAuthProvider.signin(() => {
      setUser(JSON.stringify(newUserMail));
      window.localStorage.setItem('userFromStorage', JSON.stringify(newUserMail));
      callback();
    });
  };

  let signout = (callback: VoidFunction) => {
    return fakeAuthProvider.signout(() => {
      setUser(null);
      window.localStorage.removeItem('userFromStorage');
      callback();
    });
  };
  const value = { user, signin, signout };

  // 用useEffect处理异步初始化逻辑
  useEffect(() => {
    const initAuth = async () => {
      if (user) {
        try {
          await SignInPostService(JSON.parse(user).token);
        } catch (err) {
          // 处理初始化失败情况
          window.localStorage.removeItem('userFromStorage');
          setUser(null);
        } finally {
          setIsLoading(false);
        }
      } else {
        setIsLoading(false);
      }
    };
    initAuth();
  }, [user]);

  // 加载中时返回加载组件,避免子组件提前发起请求
  if (isLoading) {
    return <div>Loading...</div>; // 替换为你的加载组件
  }

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

2. Axios全局头依赖不可靠 + 请求未等待token设置

页面刷新时Axios默认头会被重置,若某些请求在SignInPostService完成前触发,全局X-AUTH-TOKEN尚未设置,就会返回401。

修复方案:
放弃依赖Axios全局头,改为创建专用Axios实例并添加请求拦截器,动态从本地存储读取token:

// 创建专属Axios实例
const apiClient = axios.create({
  baseURL: process.env.REACT_APP_API_SERVER_URL
});

// 添加请求拦截器,自动注入token
apiClient.interceptors.request.use(config => {
  const authToken = localStorage.getItem('authToken');
  if (authToken) {
    config.headers['X-AUTH-TOKEN'] = authToken;
  }
  return config;
});

// 用该实例发起请求
export function SignInGetService() {
  apiClient.get('/url')
    .then(response => { });
}

export async function SignInPostService(token) {
  return apiClient.post('/mySignInUrl', { "token": token })
    .then(response => {     
      localStorage.setItem('authToken', response.data['data']); // 存储后端返回的token
      return response.data['data']
    }).catch(function(error) {
      // 新增401状态处理
      if(error.response.status === 500 || error.response.status === 401){
        window.localStorage.removeItem('userFromStorage');
        window.localStorage.removeItem('authToken');
        window.location.href = '/SignIn';
      }
    });
}

3. Google登录token过期未处理

你的SignInPostService仅处理500错误,若Google登录token过期,后端可能返回401,此时不会触发错误处理,导致Axios头未设置,后续请求返回401。刷新页面后Google可能自动刷新token,SignInPostService恢复正常。

修复方案:
在SignInPostService的catch中补充401状态处理:

export async function SignInPostService(token) {
  return await axios.post(process.env.REACT_APP_API_SERVER_URL + '/mySignInUrl', { "token": token })
    .then(response => {     
      axios.defaults.headers.common['X-AUTH-TOKEN'] = response.data['data'];
      return response.data['data']
    }).catch(function(error) {
      if(error.response.status === 500 || error.response.status === 401){
        window.localStorage.removeItem('userFromStorage');
        window.location.href = '/SignIn';
      }
    });
}

内容的提问来源于stack exchange,提问作者Ismael Hadj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 19:55:32