结论:Laravel + rebing/graphql-laravel 可行,但需手动调用 Sanctum::authenticate() 并在 context 中显式注入 user,否则 @can、$context->user 均失效;GraphQL 不走路由中间件,Auth 状态丢失。

直接说结论:用 Laravel + rebing/graphql-laravel 搭 GraphQL API 是可行的,但和 Sanctum 集成时,Sanctum::authenticate() 不会自动触发,必须手动调用;否则所有 @can 或 $context->user 都是 null。
GraphQL 请求不走 Laravel 中间件,Auth 状态丢失
Laravel 的 Sanctum 中间件(如 auth:sanctum)只对 HTTP 路由生效,而 rebing/graphql-laravel 默认把整个 GraphQL 请求当作一个 POST 到 /graphql,内部用 GraphQLController 处理,绕过了路由级中间件链。
结果就是:Auth::user() 始终为 null,即使请求头带了 Authorization: Bearer xxx 或已设置 Sanctum Cookie。
解决方法是手动在 GraphQL 查询/变更前注入认证逻辑:
立即学习“PHP免费学习笔记(深入)”;
- 在
config/graphql.php的'schema' => ['query' => [...]]之前,加一个全局middleware数组,但该包不支持传统中间件写法 - 更可靠的做法:在每个需要鉴权的
Query或Mutation类的resolve()方法开头,显式调用Sanctum::authenticate($request) - 或者统一在
GraphQLController@query中提前执行(需重写控制器或监听GraphQLQueryExecuted事件)
推荐后者——在 app/Http/Controllers/GraphQLController.php 中重写 query():
public function query(Request $request)
{
// 手动触发 Sanctum 认证,确保 $request->user() 可用
if ($request->hasHeader('Authorization') || $request->hasCookie('laravel_session')) {
Sanctum::authenticate($request);
}
return parent::query($request);
}
rebing/graphql-laravel 的 context 不自动包含 user
即便 $request->user() 已存在,rebing/graphql-laravel 默认也不会把它塞进 GraphQL 的 $context。这意味着你在 resolver 里写 $context->user 会报错或返回空。
必须在 config/graphql.php 中显式配置 'context' => function (Request $request) { ... }:
'context' => function (Request $request) {
// 确保 Sanctum 已运行
if (! $request->user() && $request->hasHeader('Authorization')) {
Sanctum::authenticate($request);
}
return ['user' => $request->user()];
},
注意:这个闭包只在每次请求开始时运行一次,所以要在这里完成所有上下文初始化,比如 DB::connection() 切换、租户识别等也得放这儿。
使用 @can 指令时权限检查总失败
rebing/graphql-laravel 提供的 @can 指令依赖 Illuminate\Auth\Access\Gate,但它默认从 Auth::user() 取用户——而你刚知道,这个值在 GraphQL 上下文中并不自动可用。
常见错误现象:"Unauthenticated." 或 "This action is unauthorized." 即使 token 正确、用户已登录。
根本原因不是策略写错了,而是 @can 指令没拿到 user 实例。修复方式有两个:
- 在
context配置中确保'user'键存在且非 null(见上一节) - 重写
@can指令的解析逻辑,在app/GraphQL/Directives/CanDirective.php中覆盖resolveField,从$context['user']而非Auth::user()获取主体 - 更轻量的做法:不用
@can指令,改在 resolver 内部用$context['user']->can('edit post')显式判断,可控性更强
Sanctum Token 过期时间与 GraphQL 长连接场景不匹配
Sanctum 默认 token 是长期有效的(除非手动 token()->delete()),但这对 GraphQL 客户端(尤其是移动端或桌面端)容易造成“静默失效”:用户长时间未操作,token 其实还活着,但 session cookie 可能已被浏览器清理,导致后续请求因 Cookie 缺失而认证失败。
这不是 GraphQL 特有,但 GraphQL 更容易暴露这个问题——因为前端往往复用同一个 client 实例,不会像页面跳转那样自然触发重新登录流程。
建议动作:
- 前端在收到
401响应后,主动清空本地 token 并跳转登录页(不要只弹提示) - 后端在 GraphQL resolver 中检测
!$context['user']时,统一返回自定义错误码(如UNAUTHENTICATED),避免混用500或泛化 message - 如果用 Cookie 模式,确保
SESSION_DRIVER=cookie且sanctum.stateful配置包含你的前端域名
真正麻烦的不是怎么配通,而是当 $context['user'] 在某个 resolver 里突然为 null,而你忘了它其实取决于前面那个 Sanctum::authenticate() 是否被调用过——这个点很容易漏在调试时只看策略、不查认证链路。



















