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

React首次请求遇401 Unauthorized错误,刷新后恢复问题排查

问题:React Saga项目首次请求返回401 Unauthorized,刷新页面后恢复

所有请求首次发起时均返回401错误,但刷新页面后问题消失。已确认token已正确获取并存入sessionStorage,但首次请求时token未注入请求头。


Axios配置代码

import axios from "axios";
import { api } from "../config";

// default
axios.defaults.baseURL = api.API_URL;

// content type
const token = JSON.parse(sessionStorage.getItem('authUser'))
  ? JSON.parse(sessionStorage.getItem('authUser')).data.token : null;
 
if (token) axios.defaults.headers.common["Authorization"] = 'Bearer ' + token;

// content type
axios.defaults.headers.post["Content-Type"] = 'application/x-www-form-urlencoded';
// intercepting to capture errors
axios.interceptors.response.use(
  function (response) {
    return response ? response.data : response;
  },
  function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    let message;
    switch (error.response.status) {
      case 500:
        message = "Internal Server Error";
        break;
      case 401:
        window.location.href = '/login';
        message =  "not";
        break;
      case 404:
        message = "Sorry! the data you are looking for could not be found";
        break;
      default:
        message = error.response.data.message;
    }
    return Promise.reject(message);
  }
);

/**
 * Sets the default authorization
 * @param {*} token
 */
const setAuthorization = (token) => {
  axios.defaults.headers.common["Authorization"] = "Bearer " + token;
};

class APIClient {
  /**
   * Fetches data from given url
   */
  get = (url, params) => {
    let response;
    let paramKeys = [];
    if (params) {
      Object.keys(params).map((key) => {
        paramKeys.push(key + "=" + params[key]);
        return paramKeys;
      });
      const queryString = paramKeys && paramKeys.length ? paramKeys.join("&") : "";
      response = axios.get(`${url}?${queryString}`, params);
    } else {
      response = axios.get(`${url}`, params);
    }
    return response;
  };
  /**
   * post given data to url
   */
  create = (url, data) => {
    return axios.post(url, data);
  };
  /**
   * Updates data
   */
  update = (url, data) => {
    return axios.patch(url, data);
  };

  put = (url, data) => {
    return axios.put(url, data);
  };
  /**
   * Delete
   */
  delete = (url, config) => {
    return axios.delete(url, { ...config });
  };
}
const getLoggedinUser = () => {
  const user = sessionStorage.getItem("authUser");
  if (!user) {
    return null;
  } else {
    return JSON.parse(user);
  }
};

export { APIClient, setAuthorization, getLoggedinUser };

登录Saga代码

import { call, put, takeEvery, takeLatest } from "redux-saga/effects";

// Login Redux States
import { LOGIN_USER, LOGOUT_USER, SOCIAL_LOGIN } from "./actionTypes";
import { apiError, loginSuccess, logoutUserSuccess } from "./actions";

//Include Both Helper File with needed methods
import { getFirebaseBackend } from "../../../helpers/firebase_helper";
import {
  postJwtLogout,
  postJwtLogin,
  postSocialLogin,
} from "../../../helpers/backend_helper";

import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const fireBaseBackend = getFirebaseBackend();

function* loginUser({ payload: { user, history } }) {
  try {
    if (process.env.REACT_APP_DEFAULTAUTH === "firebase") {
      const response = yield call(
        fireBaseBackend.loginUser,
        user.email,
        user.password
      );
      yield put(loginSuccess(response));
    } else if (process.env.REACT_APP_DEFAULTAUTH === "jwt") {
      const response = yield call(postJwtLogin, {
        username: user.email,
        password: user.password,
      });
      sessionStorage.setItem("authUser", JSON.stringify(response));
      if (response.status === "SUCCESS") {
        yield put(loginSuccess(response));
        history.push("/dashboard");
        window.location.reload();
      } else {
        yield put(apiError(response));
      }
    }
  } catch (error) {
    yield put(apiError(error));
    toast.error(error, { autoClose: 6000 });
  }
}
function* logoutUser() {
  try {
    sessionStorage.removeItem("authUser");
    if (process.env.REACT_APP_DEFAULTAUTH === "jwt") {
      const response = yield call(postJwtLogout);
      yield put(logoutUserSuccess(LOGOUT_USER, response));
    } else {
      yield put(logoutUserSuccess(LOGOUT_USER, true));
    }
  } catch (error) {
    yield put(apiError(LOGOUT_USER, error));
  }
}

function* socialLogin({ payload: { data, history, type } }) {
  try {
    if (process.env.REACT_APP_DEFAULTAUTH === "firebase") {
      const fireBaseBackend = getFirebaseBackend();
      const response = yield call(fireBaseBackend.socialLoginUser, data, type);
      sessionStorage.setItem("authUser", JSON.stringify(response));
      yield put(loginSuccess(response));
    } else {
      const response = yield call(postSocialLogin, data);
      sessionStorage.setItem("authUser", JSON.stringify(response));
      yield put(loginSuccess(response));
    }
    history.push("/dashboard");
  } catch (error) {
    yield put(apiError(error));
  }
}

function* authSaga() {
  yield takeEvery(LOGIN_USER, loginUser);
  yield takeLatest(SOCIAL_LOGIN, socialLogin);
  yield takeEvery(LOGOUT_USER, logoutUser);
}

export default authSaga;

问题原因及解决方法

原因

Axios配置中,Authorization请求头是在模块加载时一次性读取sessionStorage设置的。登录成功后虽将token存入sessionStorage,但此时Axios的默认请求头已初始化完成,不会自动更新,导致首次进入页面时请求未携带token,触发401。刷新页面后模块重新加载,读取到最新token,请求头设置成功,问题消失。

解决方法

方法1:登录成功后手动更新请求头

在登录Saga的loginUser函数中,存入sessionStorage后立即调用setAuthorization方法更新Axios请求头,无需刷新页面:

  1. 首先在登录Saga中导入setAuthorization:
import { setAuthorization } from "../../path-to-your-axios-config-file";
  1. 修改jwt登录分支代码:
else if (process.env.REACT_APP_DEFAULTAUTH === "jwt") {
  const response = yield call(postJwtLogin, {
    username: user.email,
    password: user.password,
  });
  sessionStorage.setItem("authUser", JSON.stringify(response));
  if (response.status === "SUCCESS") {
    // 新增:调用setAuthorization更新请求头
    yield call(setAuthorization, response.data.token);
    yield put(loginSuccess(response));
    history.push("/dashboard");
    // 移除window.location.reload();
  } else {
    yield put(apiError(response));
  }
}

方法2:使用请求拦截器动态添加token

修改Axios配置,添加请求拦截器,每次发起请求时实时从sessionStorage读取token并设置请求头:

// 在Axios配置中添加请求拦截器
axios.interceptors.request.use(
  function(config) {
    const token = JSON.parse(sessionStorage.getItem('authUser'))?.data.token;
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  function(error) {
    return Promise.reject(error);
  }
);

同时可以移除模块加载时设置请求头的代码:

// 移除以下代码
// const token = JSON.parse(sessionStorage.getItem('authUser'))
//   ? JSON.parse(sessionStorage.getItem('authUser')).data.token : null;
//  
// if (token) axios.defaults.headers.common["Authorization"] = 'Bearer ' + token;

内容的提问来源于stack exchange,提问作者Serija Nada

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 22:10:42