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

Axios无法将Access Token存入请求头的授权问题求助

授权请求头设置异常问题

登录流程可正常完成,refresh token已成功存入Cookie和数据库,但Axios无法将access token写入请求头。浏览器网络面板完全看不到Authorization请求头,尝试通过axios.default.headers设置全局请求头也无效;执行console.log(axios.defaults.headers.common)能在控制台看到已设置的token,但实际请求中就是不携带。


前端Login组件代码

export default function Login () {
  const [state, setState] = useContext(UserContext);
  const navigate = useNavigate()

  const { register, handleSubmit, formState: { errors } } = useForm<Inputs>({
    defaultValues: loginValues,
    mode: 'onTouched',
    criteriaMode: 'firstError',
    reValidateMode: 'onBlur'
  });

  const onSubmit: SubmitHandler<Inputs> = async (data) => {
    try {
      const phone_num = data.phone_num; // eslint-disable-line
      const password = data.password;

      const { data: login } = await axios.post("auth/login_admin", {
        phone_num,
        password
      },
      { withCredentials: true }
      );

      axios.defaults.headers.common['Authorization'] = `Bearer ${login['token']}`;

      setState({
        data: {
          id: login.data.id,
          isAdmin: login.data.isAdmin,
          user_type: login.data.user_type
        },
        loading: false,
        error: null
      })

      navigate("/council");
    } catch (err: any) {
      setState({
        data: null,
        loading: false,
        error: err.message
      })
      console.log(err)
    }
  };

  return (
    <>
      <Box
        sx={{
          display: 'center',
          justifyContent: 'center',
          alignItems: 'center',
          width: '100vw',
          height: '100vh'
        }}
      >
        <Box
          sx={{
            height: 'auto',
            width: '40%',
            display: 'center',
            justifyContent: 'center',
            alignItems: 'center'
          }}
          component='form'
        >
          <TextField
            margin="normal"
            fullWidth
            variant='filled'
            id="phone_num"
            label="Phone Number"
            {...register("phone_num", {
              required: 'Phone Number is required',
              pattern: {
                value: phonePattern,
                message: 'Please enter a valid phone number'
              }
            })}
            autoFocus
            error={!!errors?.phone_num}
          />
          {errors.phone_num &&
              (<Typography variant='body2' mt={1}
              component='span' sx={{ color: 'red', textAlign: 'left' }}
              >{errors.phone_num?.message}</Typography>)
          }
          <FormControl sx={{ mt: 3, width: '100%' }} variant="filled">
            <InputLabel htmlFor="filled-adornment-password">Password</InputLabel>
            <FilledInput
              id="filled-adornment-password"
              // type={passType}
              {...register("password", {
                required: 'Password is required',
                pattern: {
                  value: passwordPattern,
                  message: 'Password cannot be less than 8 characters'
                }
              })}
              error={!!errors?.password}
            />
          </FormControl>
          <Button
            type="submit"
            onClick={handleSubmit(onSubmit)} // eslint-disable-line
          >
            Sign In
          </Button>
        </Box>
      </Box>
    </>
  )
}

后端相关代码

Service层代码

export const adminLogin = async (res: Response, body: AuthType, next: NextFunction) => {
  let transaction;
  try {
    transaction = await sequelize.transaction();
    const { phone_num, password } = body;
    const user = await Admin.findOne({ where: { phone_num } }, { transaction });
    if (!user || user === null) {
      return next(new AppError("Invalid phone number or password", BAD_REQUEST));
    }

    const isMatch = verifyBcryptPassword(password, user.password);
    if (!isMatch) {
      return next(new AppError("Invalid phone number or password", BAD_REQUEST));
    }

    await transaction.commit();
    const { token, refreshToken } = await jwtGenerator(user.dataValues);

    res.cookie('refreshToken', refreshToken, {
      httpOnly: true,
      maxAge: 7 * 24 * 60 * 60 * 1000 //7 days
    });

    return { user, token };
  } catch (e: any) {
    if (transaction) {
      await transaction.rollback();
    }
    return next(new AppError(e, BAD_REQUEST));
  }
};

Controller层代码

const login_admin = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const validate = authValidator.authLogin(req.body);
    if (validate.error) {
      return next(new AppError(validate.error.message, BAD_REQUEST));
    }

    const result = await adminLogin(res, req.body, next);
    const user = result?.user.dataValues;
    const access = result?.token;

    return res.status(OK).json({
        status: "success",
        message: "Login was successful.",
        token: access,
        data: {
          id: user.id,
          isAdmin: user.isAdmin,
          user_type: user.user_type
        }
      });
  } catch (error: any) {
    return next(new AppError(error.message, BAD_REQUEST));
  }
};

问题排查与解决方法

1. 检查Axios实例一致性

如果项目中使用了自定义Axios实例(通过axios.create()创建),直接修改全局axios.defaults不会作用于该实例。解决:

  • 确保所有请求复用同一个实例,并修改该实例的headers:
// 示例:创建全局Axios实例
const api = axios.create({ baseURL: '/api' });

// 登录成功后设置实例headers
api.defaults.headers.common['Authorization'] = `Bearer ${login.token}`;

2. 持久化Token并添加请求拦截器

全局设置的headers在页面刷新后会丢失,且路由跳转可能导致状态重置。正确做法是:

  • 将Token存入localStorage,并通过请求拦截器自动注入:
// 项目入口文件添加拦截器
axios.interceptors.request.use(
  config => {
    const token = localStorage.getItem('accessToken');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  error => Promise.reject(error)
);

// 登录时存入Token
const onSubmit = async (data) => {
  // ...登录请求逻辑
  localStorage.setItem('accessToken', login.token);
  // 同步设置全局headers
  axios.defaults.headers.common['Authorization'] = `Bearer ${login.token}`;
  // ...其他逻辑
};

3. 验证后端CORS配置

后端必须允许Authorization请求头通过CORS,否则浏览器会自动移除该头。修改CORS配置:

const corsOptions = {
  origin: '你的前端域名',
  credentials: true,
  allowedHeaders: ['Content-Type', 'Authorization'] // 必须包含Authorization
};
app.use(cors(corsOptions));

4. 确认代码执行顺序

确保axios.defaults.headers的设置在后续请求之前执行。可添加测试请求验证:

axios.defaults.headers.common['Authorization'] = `Bearer ${login.token}`;
// 立即发起测试请求
axios.get('/api/test-endpoint').then(res => console.log('请求头已携带Token'));

内容的提问来源于stack exchange,提问作者Ayobami Ajibola

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 14:50:47