Laravel自动添加Authorization令牌中间件问题求助
问题分析
你遇到的核心问题是中间件执行顺序错误:auth中间件(默认是web守卫,会触发登录页重定向)在你的AutoAssignGuestToken中间件之前执行,导致请求还没来得及附加Authorization头,就被拦截重定向了。另外现有实现还存在几个逻辑漏洞:
- API路由误用web守卫,不符合无状态API的特性,会触发不必要的重定向
- 访客用户创建逻辑未复用已有有效账号,会导致数据库堆积大量重复Guest用户
- 手动拆分验证token的方式易出错,Laravel Sanctum自带完善的token验证机制
解决方案
1. 调整中间件优先级
在app/Http/Kernel.php中,将AutoAssignGuestToken注册到api中间件组,并且放在auth:sanctum之前,确保token先被附加再执行认证:
protected $middlewareGroups = [ 'api' => [ \App\Http\Middleware\AutoAssignGuestToken::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'auth:sanctum', // 必须放在自动分配中间件之后 ], ];
2. 优化中间件实现代码
以下是修复后的完整代码,解决了重定向、重复创建用户、token验证等问题:
<?php namespace App\Http\Middleware; use App\Models\User; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Laravel\Sanctum\PersonalAccessToken; use Symfony\Component\HttpFoundation\Response; class AutoAssignGuestToken { public function handle(Request $request, Closure $next): Response { $bearerToken = $request->bearerToken(); $validToken = false; // 验证请求头中的token是否有效 if ($bearerToken) { $token = PersonalAccessToken::findToken($bearerToken); $validToken = $token && !$token->expired(); } // 请求头token无效时,尝试从cookie读取访客token并验证 if (!$validToken) { $guestToken = $request->cookie('guest_token'); if ($guestToken) { $token = PersonalAccessToken::findToken($guestToken); $validToken = $token && !$token->expired(); if ($validToken) { $request->headers->set('Authorization', 'Bearer ' . $guestToken); Auth::guard('sanctum')->loginUsingId($token->tokenable_id); } } } // 无有效token时,创建新访客账号和token if (!$validToken) { $bearerToken = $this->createGuestToken($request); } $response = $next($request); // 新创建token时,写入cookie保存(httpOnly提升安全性) if (isset($bearerToken)) { $response->withCookie(cookie('guest_token', $bearerToken, 60 * 24 * 7, null, null, false, true)); // 如需在响应返回token,可取消下方注释 // $content = json_decode($response->getContent(), true); // $content['data']['token'] = $bearerToken; // $response->setContent(json_encode($content)); } return $response; } protected function createGuestToken(Request $request): string { // 创建带标记的访客用户(建议给User模型加is_guest字段,方便后续区分) $guest = User::create([ 'name' => 'Guest_' . uniqid(), 'email' => 'guest_' . uniqid() . '@example.com', // 若User模型必填email,生成临时邮箱 'is_guest' => true, ]); // 使用Sanctum创建API token $plainTextToken = $guest->createToken('Guest Token')->plainTextToken; // 给请求附加Authorization头并登录访客用户 $request->headers->set('Authorization', 'Bearer ' . $plainTextToken); Auth::guard('sanctum')->login($guest); return $plainTextToken; } }
3. 关键改动说明
- 守卫切换:改用
auth:sanctum守卫,避免API请求被重定向到登录页,符合无状态API特性 - token验证:使用Sanctum自带的
PersonalAccessToken::findToken()方法验证,比手动拆分更可靠 - 访客复用:优先验证cookie中的访客token,有效则直接复用,避免数据库冗余
- 安全优化:设置cookie为
httpOnly,防止前端XSS窃取token - 顺序保障:确保自动分配中间件在认证中间件之前执行,保证认证逻辑触发前请求已携带有效token
4. 路由配置示例
确保API路由使用api中间件组:
Route::middleware(['api'])->group(function () { // 需要认证的API路由示例 Route::get('/protected-data', function () { return response()->json(['user' => auth()->user(), 'message' => '访问受保护数据成功']); }); });
内容的提问来源于stack exchange,提问作者ThatGuy24
相关产品推荐
相关产品推荐

