Laravel 11多租户登录遇「Invalid key supplied」错误求助
问题分析与解决方案
使用Laravel 11、Laravel Passport和Tenancy for Laravel实现多租户独立数据库登录时,抛出Invalid key supplied错误,核心原因是Passport默认加载全局密钥文件,未针对租户上下文切换密钥来源,同时部分Auth配置存在疏漏。以下是针对性解决方案:
1. 修正Auth Guard配置
你的tenant Guard未关联对应的用户提供者,且模型路径格式错误,修正config/auth.php:
'guards' => [ 'tenant_web' => [ 'driver' => 'session', 'provider' => 'tenant_users', ], 'tenant' => [ 'driver' => 'passport', 'provider' => 'tenant_users', // 关联租户用户提供者 'model' => App\Models\Tenant\User::class, // 修正为完整模型类路径 ], ], 'providers' => [ 'main_users' => [ 'driver' => 'eloquent', 'model' => env('AUTH_MODEL', App\Models\User::class), ], 'tenant_users' => [ 'driver' => 'eloquent', // 此处改为eloquent驱动(原'tenant'驱动不存在) 'model' => App\Models\Tenant\User::class, ], ],
2. 确保租户数据库包含Passport表
每个租户数据库需要独立的Passport OAuth表,在租户播种器中添加Passport表初始化逻辑:
// 租户播种器文件(如database/seeders/TenantDatabaseSeeder.php) use Illuminate\Support\Str; use Laravel\Passport\Client; use Laravel\Passport\Migrations\CreateOauthTables; public function run() { // 运行Passport迁移创建OAuth表 $this->call(CreateOauthTables::class); // 创建密码授权客户端 Client::create([ 'name' => 'Password Grant Client', 'secret' => Str::random(40), 'redirect' => config('app.url'), 'personal_access_client' => false, 'password_client' => true, 'revoked' => false, ]); // 创建个人访问客户端 Client::create([ 'name' => 'Personal Access Client', 'secret' => Str::random(40), 'redirect' => config('app.url'), 'personal_access_client' => true, 'password_client' => false, 'revoked' => false, ]); }
3. 为每个租户生成独立Passport密钥
Passport默认使用全局storage/oauth-*.key文件,多租户下需为每个租户生成专属密钥,并在租户初始化时加载:
步骤1:注册租户创建事件监听
在app/Providers/EventServiceProvider.php中添加事件监听:
protected $listen = [ \Stancl\Tenancy\Events\TenantCreated::class => [ \App\Listeners\GenerateTenantPassportKeys::class, ], ];
步骤2:编写密钥生成监听类
创建app/Listeners/GenerateTenantPassportKeys.php:
namespace App\Listeners; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\File; use Stancl\Tenancy\Events\TenantCreated; class GenerateTenantPassportKeys { public function handle(TenantCreated $event) { $tenantId = $event->tenant->id; $keyPath = storage_path("tenants/{$tenantId}/oauth"); // 创建租户密钥存储目录 if (!File::exists($keyPath)) { File::makeDirectory($keyPath, 0755, true); } // 生成RSA密钥对 $privateKey = openssl_pkey_new([ 'private_key_bits' => 2048, 'private_key_type' => OPENSSL_KEYTYPE_RSA, ]); openssl_pkey_export_to_file($privateKey, "{$keyPath}/oauth-private.key"); $publicKeyDetails = openssl_pkey_get_details($privateKey); File::put("{$keyPath}/oauth-public.key", $publicKeyDetails['key']); openssl_pkey_free($privateKey); } }
步骤3:动态加载租户密钥
创建app/Providers/PassportTenantServiceProvider.php:
namespace App\Providers; use Illuminate\Support\ServiceProvider; use Laravel\Passport\Passport; use Stancl\Tenancy\Facades\Tenancy; class PassportTenantServiceProvider extends ServiceProvider { public function boot() { // 租户初始化时加载对应密钥 Tenancy::hook('initialize', function ($tenant) { $keyPath = storage_path("tenants/{$tenant->id}/oauth"); Passport::loadKeysFrom($keyPath); }); } }
在config/app.php的providers数组中注册该服务提供者。
4. 修正登录逻辑
确保登录时使用租户Guard,并指定租户数据库中的Passport客户端:
// Controllers/AuthController.php public function store(Request $request) { $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); $guard = auth()->guard('tenant'); $success = $guard->attempt([ 'email' => $request->email, 'password' => $request->password, ]); if ($success) { // 获取租户数据库中的个人访问客户端 $client = \Laravel\Passport\Client::where('personal_access_client', true)->first(); // 创建令牌时指定客户端ID $token = $guard->user()->createToken('authToken', [], $client->id)->accessToken; return response([ 'success' => true, 'token' => $token ], 200); } return response(["message" => "Password mismatch"], 422); }
内容的提问来源于stack exchange,提问作者Sven Nijkamp
相关产品推荐
相关产品推荐

