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

Laravel多租户多库测试:动态MySQL用户访问被拒问题

Laravel stancl/tenancy v3.7.2 租户测试权限问题解决方案

问题背景

使用Laravel + stancl/tenancy v3.7.2开发多租户应用,采用PermissionControlledMySQLDatabaseManager管理租户数据库、用户及密码,通过Tenant模型观察者自动生成租户凭证。运行租户专属测试时频繁出现以下错误:

SQLSTATE[HY000] [1045] Access denied for user 'testing_t_YhIAxUn1z5L3y3qB'@'172.26.0.5' (using password: YES)...

已确认租户用户和数据库能正常创建,Pest的beforeEach钩子日志显示连接凭证、租户信息、数据库表均正确,但测试内无法访问;测试报错时租户删除操作不稳定,导致大量库和用户残留;改用root账号测试可稳定通过,但不符合生产环境逻辑。

解决方案

1. 修正MySQL用户授权的主机范围

测试环境若使用容器(如报错中的172.26.0.5为容器IP),创建租户用户时可能仅授权了localhost或固定IP,导致跨容器访问被拒。

  • 调整PermissionControlledMySQLDatabaseManager的配置,确保创建用户时主机参数设为%(允许所有主机访问),或匹配测试容器的IP段。
  • 可在config/tenancy.php中修改数据库管理器配置:
    'database' => [
        'manager' => \Stancl\Tenancy\DatabaseManagers\PermissionControlledMySQLDatabaseManager::class,
        'options' => [
            'create_tenant_user' => true,
            'tenant_user_host' => '%', // 测试环境设为%,生产可按需限制
        ],
    ],
    

2. 强制刷新租户数据库连接配置

测试中创建租户后,Laravel可能缓存了旧的数据库连接配置,导致使用无效凭证访问。在TenantTestCase的beforeEach中添加连接刷新逻辑:

$this->tenant = $this->createTenant(); // 你的创建租户方法
tenancy()->initialize($this->tenant);

// 清理旧连接,加载最新租户配置
DB::purge('tenant');
config()->set('database.connections.tenant', $this->tenant->database->toArray());
DB::reconnect('tenant');

3. 确保异常时的租户清理稳定性

测试失败时,租户连接可能已失效,导致无法正常删除租户。改用主连接(超级管理员权限)执行清理,避免残留:

afterEach(function () {
    // 切换回主系统连接
    DB::purge('tenant');
    DB::reconnect('system'); // 假设system是主连接的名称

    if (isset($this->tenant)) {
        try {
            $this->tenant->delete();
        } catch (\Exception $e) {
            // 手动清理数据库和用户,确保彻底删除
            $dbName = $this->tenant->database->database;
            $userName = $this->tenant->database->username;
            DB::connection('system')->statement("DROP DATABASE IF EXISTS `{$dbName}`");
            DB::connection('system')->statement("DROP USER IF EXISTS `{$userName}`@'%'");
        }
    }
});

4. 添加权限生效延迟/重试机制

MySQL创建用户并授权后,可能存在短暂的权限同步延迟,导致测试立即访问时失败。添加重试逻辑:

// 创建并初始化租户后执行
$maxRetries = 3;
$retryCount = 0;
do {
    try {
        DB::connection('tenant')->getPdo(); // 验证连接有效性
        break;
    } catch (\Exception $e) {
        $retryCount++;
        if ($retryCount >= $maxRetries) {
            throw $e;
        }
        usleep(500000); // 延迟500ms后重试
    }
} while (true);

5. 验证连接配置的准确性

在beforeEach中打印实际生效的数据库连接配置,确认没有被其他逻辑覆盖:

// 初始化租户后打印配置
dd(config('database.connections.tenant'));

检查username、password、database、host字段是否与租户实际生成的凭证一致。

内容的提问来源于stack exchange,提问作者Winter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 05:42:46