Sanctum鉴权必须严格区分SPA(auth:sanctum+Cookie/CSRF)和纯API(auth:sanctum+Bearer Token)两种场景,混用或配置错误(如中间件未注册、域名未列入SANCTUM_STATEFUL_DOMAINS、未设withCredentials:true等)将直接导致401/419。

Sanctum 不是“装上就能用”的鉴权方案,它分两种模式:带 session 的 SPA 场景(走 cookie + CSRF),和纯 token 的 API 场景(走 Bearer header)。混用或配置错一种,就会 401 或 419,且错误信息不提示真实原因。
auth:sanctum 中间件为什么总返回 401?
这不是 token 错了,而是中间件根本没进验证逻辑。常见原因有三个:
-
auth:sanctum路由没配在api中间件组里,而你又没手动加EnsureFrontendRequestsAreStateful -
config/auth.php里'guards' => ['api' => ['driver' => 'sanctum']]没显式设 driver,仍为默认的token或session - 前端请求没带
credentials: 'include'(fetch)或withCredentials: true(Axios),导致 cookie 传不过去,EnsureFrontendRequestsAreStateful直接跳过 session 解析
createToken() 返回的 token 怎么总是 null?
$user->createToken('api')->plainTextToken 只能读一次。这个属性不是数据库字段,是构造时临时生成的明文副本,对象序列化或下次访问就没了。
- 必须在创建后立刻取值:
$token = $user->createToken('api'); return ['token' => $token->plainTextToken]; - 不能先存对象再取:
$token->plainTextToken在 response 外调用基本为空 - 数据库里存的是
token字段(哈希值),前端永远拿不到原始值——plainTextToken就是那唯一一次机会
本地开发时登录成功但后续请求全 401
问题不在 token,而在 session 共享失败。Laravel 默认用 file 或 database 存 session,跨域时 cookie 无法绑定到正确 session_id。
-
.env必须设:SESSION_DRIVER=cookie(不是redis或database) -
SANCTUM_STATEFUL_DOMAINS=localhost:5173(Vue/React 端口,不能带http://) -
config/cors.php中'supports_credentials' => true且'allowed_origins'写具体地址,不能填* - 前端 fetch 必须加
{ credentials: 'include' },否则浏览器压根不发laravel_session和XSRF-TOKEN
怎么让 Sanctum 支持 Admin 和 User 两个模型?
默认只认 User,强行改模型会导致 tokenable_type 存错类名、tokenable_id 查不到记录。
- 新建
Admin模型,实现Authenticatable接口,加HasApiTokenstrait - 在
config/auth.php新增 guard:'admin-api' => ['driver' => 'sanctum', 'provider' => 'admins'] - 重写
PersonalAccessToken类,覆盖tokenableType()方法返回App\Models\Admin::class - 迁移中把
tokenable_type字段长度改成255(默认 191 不够存完整命名空间)
最常被忽略的点:中间件顺序。如果 EnsureFrontendRequestsAreStateful 不在 $middlewareGroups['api'] 最前面,CSRF cookie 就设不上,整个 SPA 流程从第一步就断掉——但错误日志里不会告诉你缺了这行。


















