sanctum 令牌通过 $user->createtoken(‘login’)->plaintextToken 生成并返回明文 token 给前端;须配置 sanctum_stateful_domains、正确注册中间件、按场景选择 cookie 或 bearer 方式安全传递,并主动调用 revoke 管理生命周期。

Sanctum 令牌怎么生成并返回给前端
生成令牌不是靠手动写 sql 或硬编码,而是调用 createToken() 方法。它会自动创建加密 token、存进数据库(personal_access_tokens 表),并返回一个带明文令牌的 AccessToken 实例——这个明文值必须在登录成功后立刻发给前端,之后就再也看不到了。
常见错误是生成后没取 plainTextToken,而是直接返回整个模型,导致前端拿到的是空字符串或报错 Object of class laravelSanctumNewAccessToken could not be converted to String。
- 登录成功后必须用
$user->createToken('login')->plainTextToken取值 - 不要用
$user->tokens()->create(...)手动建,会绕过 Sanctum 的哈希和作用域逻辑 - 如果用了
tokenable_type自定义模型(比如不是User),要确保HasApiTokenstrait 已正确引入
Laravel 10+ 中 Sanctum 中间件为啥不生效
最常踩的坑是中间件注册顺序不对,或者用了错误的中间件别名。Sanctum 默认依赖 EnsureFrontendRequestsAreStateful 来判断是否走 session 认证,而它只对匹配 localhost、127.0.0.1 或你配置的 SANCTUM_STATEFUL_DOMAINS 的请求放行。如果你前端跑在 http://test.local:3000,但没加进环境变量,就会静默 fallback 到 token 认证,而你又没传 Authorization: Bearer xxx,结果就是 401。
- 检查
.env是否设置了SANCTUM_STATEFUL_DOMAINS=test.local(多个用逗号分隔,不要带协议或端口) -
api路由组必须用middleware(['auth:sanctum']),不能只写auth - 确认
app/Http/Kernel.php的api中间件组里包含throttle:api和auth:sanctum,且顺序无误
前端怎么安全存和传 Sanctum token
token 是长期有效的(除非手动 revoke),所以不能存在 localStorage——xss 一触发就全丢。推荐存在 httpOnly cookie 是 Sanctum 默认方案,但前提是前后端同域或已配好跨域信任;如果必须跨域(比如 Vue 前端在 port 3000,Laravel 在 port 8000),就得改用 Bearer header 方式,此时 token 必须由前端 js 管理,那就只能退而求其次存在内存变量 + 登出清空,避免写入持久化存储。
- 使用 cookie 方式时,确保响应头有
Set-Cookie: laravel_session=xxx; expires=...; path=/; secure; httponly; samesite=lax - 用 Bearer 方式时,每次请求必须手动加
Authorization: Bearer <code>token,axios 可通过defaults.headers.common['Authorization']设置 - 不要把 token 拼在 URL 里传,容易被日志、代理、Referer 泄露
revokeToken() 不起作用?查这三处
调用 $user->currentAccessToken()->delete() 或 $user->tokens()->delete() 后仍能访问,大概率是缓存或 token 解析没走数据库。Sanctum 默认用 DatabaseTokenProvider,但如果你在 config/sanctum.php 里改过 guard 或 provider,或自定义了 PersonalAccessToken 模型但没重写 findToken(),就会跳过实际校验。
- 确认
config/sanctum.php的stateful域名列表和当前请求 Host 完全匹配(大小写、有无 www 都算不同) - 检查
PersonalAccessToken模型是否仍继承HasAbilities和HasFactory,否则can()或delete()可能静默失败 - 执行
php artisan config:clear和php artisan cache:clear,避免旧配置残留
token 的生命周期管理不像 Passport 那样自带 TTL,它完全依赖你主动 delete,这点很容易被忽略——用户登出、密码修改、设备解绑这些场景都得显式调用 revoke,不能指望“过期自动失效”。