如何限制Laravel Passport令牌仅允许白名单前端访问?
问题描述
技术栈:
- ReactJs(前端)
- Laravel(后端-API)
当前使用Laravel Passport的Personal Access Tokens实现API认证,登录代码如下:
public function login(LoginRequest $request) { $request->authenticate(); // 根据请求中的email和password验证用户 /** @var User $user */ $user = Auth::user(); $token = $user->createToken('auth'); return response([ 'message' => 'success', 'user' => $user, 'token' => $token->accessToken, ]); }
该方案运行正常,但存在问题:任何人都可通过Postman等工具获取令牌并访问受保护路由,不符合仅允许白名单前端(如React SPA)访问的需求。曾尝试在config/cors.php中配置域名白名单,该方法仅对现代浏览器有效,Postman等工具仍可正常访问,寻求可行实现方案。
可行解决方案
1. 使用Laravel Passport的Authorization Code Grant(授权码模式)+ PKCE
这是SPA场景下的标准安全方案,相比Personal Access Token,它要求前端必须通过规范的授权流程获取令牌,PKCE机制能有效防止授权码被拦截,大幅提升安全性。
配置步骤:
- 在Laravel中创建SPA类型的Passport客户端:
php artisan passport:client --spa - 前端实现PKCE授权流程:生成随机的
code_verifier和code_challenge,跳转到Laravel的授权页面,用户登录后获取授权码,再用授权码兑换令牌。 - 后端配置:确保Passport路由正确注册,在
AuthServiceProvider的boot方法中添加:Passport::routes(); Passport::enableImplicitGrant(); - 此方案下,只有白名单域名下的前端能完成完整授权流程,Postman等工具无法模拟SPA的PKCE流程(伪造成本极高)。
2. 验证请求来源的Origin和Referer头(补充防护)
虽然Postman可以伪造这些请求头,但结合其他机制能进一步提升防护等级:
- 创建自定义中间件
VerifyFrontendOrigin:<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class VerifyFrontendOrigin { protected $allowedOrigins = [ 'https://your-react-app-domain.com', 'http://localhost:3000', // 开发环境域名 ]; public function handle(Request $request, Closure $next) { $origin = $request->header('Origin'); $referer = $request->header('Referer'); // 检查Origin是否在白名单,或Referer包含白名单域名 if (!$origin || !in_array($origin, $this->allowedOrigins)) { if (!$referer || !collect($this->allowedOrigins)->contains(fn($domain) => str_contains($referer, $domain))) { return response()->json(['message' => 'Unauthorized origin'], 403); } } return $next($request); } } - 在
app/Http/Kernel.php中注册中间件,并应用到受保护的API路由组:protected $routeMiddleware = [ // ...其他中间件 'verify.origin' => \App\Http\Middleware\VerifyFrontendOrigin::class, ]; - 路由配置示例:
Route::middleware(['auth:api', 'verify.origin'])->group(function () { // 所有受保护的API路由 });
3. 令牌绑定前端设备/会话信息
在生成令牌时绑定前端的唯一标识,每次请求时验证该标识,即使令牌被窃取,也无法在其他环境使用:
- 修改登录代码,添加额外信息到令牌:
$token = $user->createToken('auth', [], [ 'frontend_id' => $request->header('X-Frontend-ID'), // 前端生成的唯一ID,存储在localStorage中 'user_agent' => $request->userAgent(), ]); - 创建验证中间件:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Laravel\Passport\Token; class VerifyTokenBinding { public function handle(Request $request, Closure $next) { $token = $request->bearerToken(); if (!$token) { return response()->json(['message' => 'Unauthorized'], 401); } $accessToken = Token::findToken($token); if (!$accessToken) { return response()->json(['message' => 'Invalid token'], 401); } $frontendId = $request->header('X-Frontend-ID'); $userAgent = $request->userAgent(); if ($accessToken->getAttribute('frontend_id') !== $frontendId || $accessToken->getAttribute('user_agent') !== $userAgent) { return response()->json(['message' => 'Token not valid for this device'], 403); } return $next($request); } } - 前端在每次请求时携带
X-Frontend-ID头,确保令牌与当前前端环境绑定。
内容的提问来源于stack exchange,提问作者Subham Manna
相关产品推荐
相关产品推荐

