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

前端Refresh-Token返回401错误但Postman正常,求助排查

问题描述

正在为系统实现Refresh-Token功能,目标是浏览器刷新后保留系统状态,但目前刷新页面会直接跳转到登录页。系统已能正常生成Refresh-Token,但前端调用刷新接口时返回401 Unauthorized错误,使用Postman发送相同请求却能正常运行。


相关代码

AuthController.php

public function refreshToken(Request $request)
{
    if (!$request->user()) {
        return response()->json(['message' => 'Unauthorized'], 401);
    }

    $request->user()->tokens()->delete(); // 吊销用户所有令牌

    $remember_token = $request->user()->createToken('remember_token')->plainTextToken;
    $expiresAt = now()->addMinute(30)->format('Y-m-d H:i:s');

    return response()->json([
        'remember_token' => $remember_token,
        'expires_at' => $expiresAt
    ]);
}

useRefreshToken Hook(JSX)

import axios from '../api/axios';
import useAuth from './useAuth';

const TOKEN_URL = '/api/refresh-token';

const useRefreshToken = () => {
const { auth, setAuth } = useAuth(); // 获取认证状态和设置方法

const refresh = async () => {
        const remember_token = localStorage.getItem('remember_token');
        const config = {
            headers: {
                Authorization: `Bearer ${remember_token}`
            }
        };
        const response = await axios.get(
            TOKEN_URL,
            config
        )
        localStorage.setItem('remember_token', response.data.remember_token);
        setAuth(prev => {
            console.log(JSON.stringify(prev));
            console.log(response.data.remember_token);
            return {
                ...prev,
                role_id: response.data.role_id,
                remember_token: response.data.remember_token
            }
        });
        return response.data.remember_token
}   
return refresh;
};

export default useRefreshToken;

PersistLogin.jsx

import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Outlet } from "react-router-dom";
import useAuth from '../hooks/useAuth';
import useRefreshToken from '../hooks/useRefreshToken';

const PersistLogin = () => {
const [isLoading, setIsLoading] = useState(true);
const refresh = useRefreshToken();
const { auth,setAuth } = useAuth();

useEffect(() => {
const verifyRefreshToken = async () => {
  try{
    const storedToken = localStorage.getItem('remember_token');
    if (storedToken) {
        setAuth(prev => ({ ...prev, remember_token: storedToken }));
        await refresh(); // 调用刷新函数更新令牌
    }
  }
  catch(err){
    console.error(err);
  }
  finally{
    setIsLoading(false);
  }
}
!auth?.remember_token ? verifyRefreshToken() : setIsLoading(false);
}, [])

useEffect(() => {
console.log(`isLoading: ${isLoading}`)
console.log(`aT:  ${JSON.stringify(auth?.remember_token)}`)
}, [isLoading])

return (
<>
  {isLoading ? <div>Loading...</div> : <Outlet/>}
</>
)

}
export default PersistLogin;

排查与解决思路

  1. 跨域请求凭证配置
    前端axios请求需开启withCredentials: true,浏览器跨域请求时默认不会携带凭证,而Postman不受此限制。检查axios全局配置或请求实例是否添加了该选项。

  2. 请求头格式校验
    确认Authorization头格式是否正确:Bearer 后必须有空格,且token无多余空格、换行符。可在前端请求前打印Authorization值,和Postman中的请求头对比。

  3. 后端认证中间件配置
    确保refresh-token路由使用了正确的认证中间件(比如Laravel的auth:sanctum),中间件需能正确解析请求头中的Bearer token。可在后端方法开头添加日志,打印接收到的Authorization头,确认token是否正确传递。

  4. LocalStorage有效性检查
    刷新页面后,先打印localStorage.getItem('remember_token'),确认存储的token和Postman中使用的一致,排除存储时的截断、格式错误问题。

  5. PersistLogin逻辑优化
    当前逻辑中setAuth是异步更新状态,但refresh直接读取LocalStorage,逻辑无问题,但可尝试将storedToken作为参数传入refresh函数,避免重复读取LocalStorage,减少潜在的竞态问题。

  6. 后端token验证逻辑
    检查$request->user()为空的原因:是否因为token已过期、格式错误,或者后端验证逻辑未正确解析请求头。可临时注释if (!$request->user())判断,打印token解析结果,定位问题。


内容的提问来源于stack exchange,提问作者Christian Medallada

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 03:05:22