Laravel 8 Sanctum个人访问令牌:Web路由鉴权重定向问题
Laravel Sanctum 令牌重定向Web路由401问题解决方案
问题核心原因
auth:sanctum中间件在Web路由场景下,默认优先检查会话(Session)认证状态,而非请求头中的Bearer令牌。- 重定向属于浏览器发起的全新请求,你在前端控制器中设置的
Authorization请求头不会被自动携带到重定向后的请求中。 - XSRF-TOKEN Cookie主要影响跨域POST/PUT/DELETE请求的合法性,重定向的GET请求若为同域,一般不是401的直接原因,但跨域场景需额外配置。
可行解决方案
方案一:将令牌存入Cookie,通过中间件自动注入请求头
这种方案让Web请求自动携带令牌,适配auth:sanctum的令牌认证逻辑:
前端处理令牌存储:拿到后端返回的
loginToken后,将其存入HttpOnly Cookie(避免XSS风险)// 假设从API响应中获取到loginToken const loginToken = response.data.loginToken; // 设置Cookie,确保路径覆盖所有受保护路由 document.cookie = `sanctum_token=${loginToken}; path=/; HttpOnly; SameSite=Lax`; // 执行重定向 window.location.href = '/admin/dashboard';自定义中间件注入Authorization头
创建app/Http/Middleware/InjectSanctumToken.php:<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class InjectSanctumToken { public function handle(Request $request, Closure $next) { // 从Cookie读取令牌,若存在则注入请求头 if ($token = $request->cookie('sanctum_token')) { $request->headers->set('Authorization', "Bearer {$token}"); } return $next($request); } }注册中间件到Web组
在app/Http/Kernel.php的web中间件组中,将自定义中间件放在auth:sanctum之前:protected $middlewareGroups = [ 'web' => [ // ...其他默认中间件 \App\Http\Middleware\InjectSanctumToken::class, \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'auth:sanctum', // ... ], ];
方案二:将令牌转换为会话登录状态
这种方案更贴合Laravel传统Web认证逻辑,把API获取的令牌转换为Session登录:
后端新增令牌转换路由
在routes/web.php中添加:use Illuminate\Http\Request; use Laravel\Sanctum\PersonalAccessToken; Route::post('/sanctum/login-with-token', function(Request $request) { $token = $request->input('loginToken'); // 验证令牌有效性 $personalToken = PersonalAccessToken::findToken($token); if (!$personalToken || $personalToken->expired()) { return back()->withErrors(['token' => '无效或过期的令牌']); } // 将令牌所属用户登录到会话 auth()->login($personalToken->tokenable); // 重定向到受保护路由 return redirect('/admin/dashboard'); });前端提交令牌到转换路由
拿到loginToken后,通过POST请求提交到上述路由,而非直接重定向:const loginToken = response.data.loginToken; // 创建表单提交(避免AJAX跨域问题,若同域也可用fetch) const form = document.createElement('form'); form.method = 'POST'; form.action = '/sanctum/login-with-token'; // 添加CSRF令牌(同域下必须) const csrfInput = document.createElement('input'); csrfInput.type = 'hidden'; csrfInput.name = '_token'; csrfInput.value = document.querySelector('meta[name="csrf-token"]').content; // 添加令牌参数 const tokenInput = document.createElement('input'); tokenInput.type = 'hidden'; tokenInput.name = 'loginToken'; tokenInput.value = loginToken; form.appendChild(csrfInput); form.appendChild(tokenInput); document.body.appendChild(form); form.submit();
关键注意事项
- 若为跨域场景,需在
config/sanctum.php中配置stateful数组,包含前端域名。 - 令牌有效期需合理设置,避免频繁过期导致用户重新登录。
- 方案二的会话登录会生成Laravel默认的
laravel_sessionCookie,后续Web请求无需再携带Bearer令牌。
内容的提问来源于stack exchange,提问作者Jorge Mauricio
相关产品推荐
相关产品推荐

