Laravel 11 Passport客户端凭证授权:不同机器的Scope配置与验证问题
解决Laravel Passport M2M客户端绑定固定Scope的问题
要给每个M2M客户端绑定固定Scope,防止它们通过scope=*获取全量权限,你需要扩展Laravel Passport的客户端凭证授权逻辑,具体步骤如下:
1. 给客户端表添加允许的Scope字段
首先创建迁移,为oauth_clients表新增存储允许Scope的字段:
php artisan make:migration add_allowed_scopes_to_oauth_clients_table --table=oauth_clients
打开生成的迁移文件,修改up方法:
public function up(): void { Schema::table('oauth_clients', function (Blueprint $table) { $table->json('allowed_scopes')->nullable(); }); }
执行迁移:
php artisan migrate
2. 为客户端配置固定Scope
直接在数据库中修改oauth_clients表的allowed_scopes字段:
- 负责发送数据的客户端:填入
["can-send"] - 负责读取数据的客户端:填入
["can-read"]
3. 自定义客户端凭证授权处理器
创建自定义控制器替换Passport默认的客户端凭证授权逻辑,强制使用客户端绑定的Scope:
<?php namespace App\Passport; use Laravel\Passport\Http\Controllers\ClientCredentialsController; use Psr\Http\Message\ServerRequestInterface; class CustomClientCredentialsController extends ClientCredentialsController { public function issueToken(ServerRequestInterface $request) { // 验证并获取当前请求的客户端实例 $client = $this->server->validateAuthenticatedClient($request); // 强制使用客户端预配置的Scope,忽略请求中的scope参数 $scopes = $client->allowed_scopes ?? []; // 生成带固定Scope的访问令牌 $token = $this->server->createToken( $client, $scopes, [] ); return response()->json($token->convertToResponseBody()); } }
4. 替换Passport默认路由
在App\Providers\AuthServiceProvider的boot方法中,替换默认的客户端凭证授权路由:
use App\Passport\CustomClientCredentialsController; use Laravel\Passport\Passport; public function boot(): void { $this->registerPolicies(); Passport::routes(function ($router) { // 保留其他默认路由 $router->forAccessTokens(); $router->forTransientTokens(); $router->forPasswordReset(); // 替换客户端凭证授权路由为自定义控制器 $router->post('/token', [CustomClientCredentialsController::class, 'issueToken']); }); // 保留原有的Scope定义 Passport::tokensCan([ 'can-send' => 'Can send data', 'can-read' => 'Can read data', ]); }
5. 可选:添加Scope校验中间件(额外安全保障)
如果需要进一步校验令牌Scope是否符合客户端权限,可自定义中间件:
php artisan make:middleware ValidateClientScopes
打开中间件文件:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Laravel\Passport\Token; use Symfony\Component\HttpFoundation\Response; class ValidateClientScopes { public function handle(Request $request, Closure $next): Response { $tokenId = $request->bearerToken() ? explode('|', $request->bearerToken())[0] : null; if (!$tokenId) { return response()->json(['error' => 'Unauthorized'], 401); } $token = Token::find($tokenId); if (!$token) { return response()->json(['error' => 'Invalid token'], 401); } $clientAllowedScopes = $token->client->allowed_scopes ?? []; $tokenScopes = $token->scopes; // 校验令牌的所有Scope都在客户端允许范围内 foreach ($tokenScopes as $scope) { if (!in_array($scope, $clientAllowedScopes)) { return response()->json(['error' => 'Scope not allowed for this client'], 403); } } return $next($request); } }
在app/Http/Kernel.php中注册中间件:
protected $routeMiddleware = [ // ... 其他中间件 'client.scopes' => \App\Http\Middleware\ValidateClientScopes::class, ];
最后给路由加上该中间件:
Route::get('/send', function (Request $request) { return 'You can send data'; })->middleware(['client:can-send', 'client.scopes']); Route::get('/read', function (Request $request) { return 'You can read data'; })->middleware(['client:can-read', 'client.scopes']);
完成以上配置后,无论客户端请求令牌时传入什么scope参数,都会被强制替换为该客户端预配置的固定Scope,彻底解决scope=*获取全量权限的问题。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

