sanctum在spa场景下依赖csrf cookie与session双机制,而非单纯发token;必须先get /sanctum/csrf-cookie获取xsrf-token,再以withcredentials:true发起登录,且ensurefrontendrequestsarestateful须置于api中间件组首位。

Sanctum 在 SPA 场景下不是“单纯发个 token 就完事”,它实际依赖 **CSRF cookie + Bearer token 双机制**。直接跳过 CSRF 步骤,哪怕 token 正确,auth:sanctum 中间件也会静默失败——这是绝大多数人卡住的第一关。
确保 EnsureFrontendRequestsAreStateful 已正确注册在 api 中间件组
这个中间件决定 Sanctum 是否走“SPA 模式”(即检查 X-XSRF-TOKEN 和 session cookie),而不是纯 token 模式。它必须出现在 app/Http/Kernel.php 的 'api' 组里,且位置靠前:
- ✅ 正确写法:
'api' => [\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api'] - ❌ 错误写法:只放在
web组、或漏掉、或放在global middleware里(会干扰非前端请求) - ⚠️ 注意:如果你的前端域名和后端不在同一顶级域(比如前端是
app.example.com,后端是api.example.com),还需在config/sanctum.php中配置'stateful' => ['app.example.com'],否则中间件直接跳过 CSRF 流程
createToken() 返回的是明文,但数据库存的是哈希值
调用 $user->createToken('login')->plainTextToken 时,返回的字符串是「一次性明文」,仅此一次可见;数据库 personal_access_tokens 表中 token 字段存的是哈希值(tokenable_id + name + 随机 salt 的组合哈希)。这意味着:
- ✅ 前端必须立刻保存
plainTextToken,后续所有请求用Authorization: Bearer <plaintexttoken></plaintexttoken> - ❌ 不能拿数据库里的
token字段值去拼 header——它解不开,也验不过 - ⚠️ 如果你用
createToken()重复生成同名 token(如都叫'login'),旧 token 会被自动删除,适合单设备登录场景;若要多设备共存,每次用不同 name,例如'login-web'/'login-mobile'
前端必须先 GET /sanctum/csrf-cookie,再发登录请求
这是 Sanctum SPA 认证最易忽略的前置步骤。不执行这一步,后端不会下发 XSRF-TOKEN cookie,后续登录或受保护请求会因 CSRF 验证失败而 419 或 401(且无明确提示)。
- ✅ 正确流程:
①
fetch('/sanctum/csrf-cookie', { method: 'GET', credentials: 'include' })② 等待响应完成(status 204),再触发登录请求 ③ 登录请求需带credentials: 'include',确保 cookie 能随请求发出 - ⚠️ Axios 用户注意:
axios.defaults.withCredentials = true必须设,否则credentials: 'include'不生效 - ⚠️ Laravel 默认已定义该路由,无需手动注册;但若你改过
config/sanctum.php中的'prefix',路径也要同步调整
auth:sanctum 路由失败时,别急着查 token——先看 cookie 和 header 是否齐全
当受保护路由返回 401 却确认 token 没错,大概率是以下三者缺一:
- 浏览器是否已持有
laravel_session和XSRF-TOKEN两个 cookie?可用 DevTools → Application → Cookies 查看 - 请求 header 是否同时包含:
Authorization: Bearer <xxx></xxx>和X-XSRF-TOKEN: <yyy></yyy>?后者由前端从 cookie 读取并手动设置(Axios 会自动读XSRF-TOKENcookie 并塞进 header,前提是xsrfCookieName和xsrfHeaderName配置匹配) - 后端是否在
VerifyCsrfToken中排除了该路由?不需要——EnsureFrontendRequestsAreStateful内部已接管 CSRF 验证,VerifyCsrfToken对api组默认不生效
真正复杂的地方在于:CSRF cookie 有时效(默认 2 小时)、会因跨域配置错误被浏览器拒绝写入、也可能被前端框架(如 Vue Router 的 history 模式)意外剥离。这些细节不报错,只让认证静默失效。











