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

跨微服务传递JWT Cookie遇401错误:Token缺失问题排查

跨微服务JWT令牌传递401问题

问题现象

直接将JWT令牌通过Cookie发送至http://localhost:8000/api/profile时可正常获取用户数据,但通过第二个微服务转发令牌时,收到401 Unauthorized错误,提示Token is missing。

错误日志

{
  "status": 401,
  "statusText": "Unauthorized",
  "headers": {
    "date": "Sat, 22 Feb 2025 08:03:25 GMT",
    "server": "uvicorn",
    "content-length": "29",
    "content-type": "application/json"
  },
  "data": {
    "detail": "Token is missing"
  }
}

相关配置与代码

发送令牌的微服务(Node.js)CORS配置

app.set('trust proxy', true);
app.use(cookieParser());

// CORS settings
app.use(cors({
  origin: 'http://localhost:5173',
  credentials: true,
  optionsSuccessStatus: 200
}));

接收令牌的微服务(FastAPI)CORS配置

app = FastAPI()

origins = [
    "http://localhost:5173"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

微服务转发请求代码

const axios = require('axios');

const getUserProfile = async (token) => {
  try {
    const response = await axios.get('http://fast-api:8000/api/profile', {
      withCredentials: true,  // Send cookies with the request
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching user profile:', error);

    if (error.response) {
      console.error('Response error data:', error.response.data);
      console.error('Response error status:', error.response.status);
      console.error('Response error headers:', error.response.headers);
    } else if (error.request) {
      console.error('Request error data:', error.request);
    } else {
      console.error('General error message:', error.message);
    }

    throw new Error('Failed to fetch user profile');
  }
};

发送端授权中间件

const authMiddleware = async (req, res, next) => {
  const token = req.cookies.access_token;

  console.log('Cookies:', req.cookies);
  console.log('Received token:', token);

  if (!token) {
    return res.status(401).json({message: 'Token is missing'});
  }

  try {
    const userProfile = await getUserProfile(token);
    req.user = userProfile;
    next();
  } catch (error) {
    console.error('Error in authMiddleware:', error);
    return res.status(401).json({
      message: error.message,
      stack: error.stack,
    });
  }
};

FastAPI接收令牌代码

@router.get("/profile")
def get_user(request: Request):
    print("Received headers:", request.headers)
    print("Received query parameters:", request.query_params)
    print("Received cookies:", request.cookies)

    token = request.cookies.get("access_token")
    print("Received token:", token)
    if not token:
        raise HTTPException(status_code=401, detail="Token is missing")

    return get_user_service(token)

核心问题

发送令牌的微服务日志显示已正确接收Cookie,但转发至接收令牌的微服务后,令牌无法被识别,返回401 Unauthorized错误。


问题原因

问题出在微服务转发请求时没有正确传递令牌:

  1. getUserProfile函数接收了token参数,但代码中完全没有使用这个参数。
  2. withCredentials: true是给浏览器发送请求时用的,用于携带浏览器端的Cookie,但微服务之间的服务器端请求不存在浏览器Cookie上下文,这个配置无效。
  3. 转发请求时没有将从客户端拿到的access_token手动添加到请求的Cookie或Header中,导致FastAPI服务无法获取到令牌。

解决方案

修改getUserProfile函数,将令牌手动传递给FastAPI服务,有两种常用方式:

方式1:通过Cookie传递

const getUserProfile = async (token) => {
  try {
    const response = await axios.get('http://fast-api:8000/api/profile', {
      headers: {
        'Cookie': `access_token=${token}`
      }
    });
    return response.data;
  } catch (error) {
    // 错误处理逻辑不变
    console.error('Error fetching user profile:', error);
    if (error.response) {
      console.error('Response error data:', error.response.data);
      console.error('Response error status:', error.response.status);
      console.error('Response error headers:', error.response.headers);
    } else if (error.request) {
      console.error('Request error data:', error.request);
    } else {
      console.error('General error message:', error.message);
    }
    throw new Error('Failed to fetch user profile');
  }
};

方式2:通过Authorization Header传递(更推荐)

如果可以修改FastAPI的令牌接收逻辑,改用Bearer Token方式更符合REST规范:

首先修改FastAPI代码:

from fastapi import HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

security = HTTPBearer()

@router.get("/profile")
def get_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    token = credentials.credentials
    if not token:
        raise HTTPException(status_code=401, detail="Token is missing")
    return get_user_service(token)

然后修改转发函数:

const getUserProfile = async (token) => {
  try {
    const response = await axios.get('http://fast-api:8000/api/profile', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });
    return response.data;
  } catch (error) {
    // 错误处理逻辑不变
    console.error('Error fetching user profile:', error);
    if (error.response) {
      console.error('Response error data:', error.response.data);
      console.error('Response error status:', error.response.status);
      console.error('Response error headers:', error.response.headers);
    } else if (error.request) {
      console.error('Request error data:', error.request);
    } else {
      console.error('General error message:', error.message);
    }
    throw new Error('Failed to fetch user profile');
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 05:44:52