You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:27:46