Next.js 15服务端组件用fetch调用Laravel 12受保护路由返回401
解决Next.js 15服务端组件调用Laravel Sanctum受保护路由返回401的问题
你的问题出在服务端请求的Cookie处理逻辑、XSRF-TOKEN解码,以及Sanctum跨域配置上,以下是具体修复方案:
1. 正确传递所有必要Cookie并解码XSRF-TOKEN
Laravel Sanctum验证SPA请求需要两个核心Cookie:laravel_session 和 XSRF-TOKEN,其中XSRF-TOKEN是URL编码状态,服务端读取后必须解码才能使用,同时请求头需要携带前缀为X-的X-XSRF-TOKEN。
修改服务端组件的fetch代码如下:
// Server Component (layout.tsx) import { cookies } from "next/headers"; export default async function ModuleLayout({ children, }: { children: React.ReactNode; }) { try { const cookieStore = await cookies(); const laravelSession = cookieStore.get("laravel_session")?.value; const encodedXsrfToken = cookieStore.get("XSRF-TOKEN")?.value; // 解码URL编码的XSRF-TOKEN const decodedXsrfToken = encodedXsrfToken ? decodeURIComponent(encodedXsrfToken) : ""; const response = await fetch("http://localhost:8000/api/fetch-user", { headers: { "Content-Type": "application/json", "X-XSRF-TOKEN": decodedXsrfToken, // 组装完整Cookie头,包含两个必要Cookie Cookie: `laravel_session=${laravelSession}; XSRF-TOKEN=${encodedXsrfToken}`, }, credentials: "include", }); console.log("response ", response); } catch (err) { console.log("err", err instanceof Error ? err.message : "Unknown error"); } return children; }
2. 检查Laravel Sanctum与Session配置
确保Laravel .env 文件中跨域参数配置正确:
# 允许的状态域(Next.js域名,多域名用逗号分隔) SANCTUM_STATEFUL_DOMAINS=localhost:3000,localhost:8000 # Session共享域名,本地开发可设为".localhost" SESSION_DOMAIN=.localhost SESSION_DRIVER=cookie
同时确认config/sanctum.php的stateful数组包含Next.js域名:
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost:3000,localhost:8000')),
3. 服务端与客户端请求的核心差异
- 客户端axios的
withXSRFToken: true会自动完成XSRF-TOKEN解码并设置X-XSRF-TOKEN头,这一步在服务端组件中需要手动实现。 - 服务端组件的
fetch运行在Node.js环境,不会自动携带Cookie,必须显式设置credentials: "include"并手动组装Cookie头。
4. 调试建议
- 打印
laravelSession和decodedXsrfToken值,确认不为空。 - 在Laravel的
app/Http/Middleware/VerifyCsrfToken.php中添加日志,检查请求携带的Cookie和头是否正确:
public function handle($request, Closure $next) { Log::info('Request Cookies:', $request->cookies->all()); Log::info('Request Headers:', $request->headers->all()); return parent::handle($request, $next); }
内容的提问来源于stack exchange,提问作者Jems
相关产品推荐
相关产品推荐

