Laravel中Sanctum认证前基于db_slug切换客户端数据库的实现问题
问题根因
Laravel 默认存在中间件优先级规则,auth 类认证中间件的优先级高于普通自定义中间件,即使你在路由组中将 clientDB 写在 auth:sanctum 前面,实际执行时仍会优先运行认证逻辑,此时数据库尚未切换,自然会从主库查询用户导致认证失败。
解决方案
第一步:调整中间件优先级
打开 app/Http/Kernel.php,找到 $middlewarePriority 属性,将你的自定义中间件添加到 Authenticate 认证中间件之前,强制它优先执行:
protected $middlewarePriority = [ \App\Http\Middleware\ClientDBMiddleware::class, // 新增到数组最顶部 \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, // 其余原有配置保持不变 ];
第二步:优化中间件逻辑
原中间件缺少校验和重连逻辑,优化后代码如下:
namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use DB; use Config; class ClientDBMiddleware { public function handle(Request $request, Closure $next) { // 校验必填参数 if(!$request->has('db_slug')){ abort(400, '缺少租户参数'); } $dbSlug = $request->input('db_slug'); // 可选:从主库租户表校验db_slug合法性,避免非法请求 // if (!\App\Models\Tenant::where('slug', $dbSlug)->exists()) { // abort(404, '租户不存在'); // } // 重置连接、更新配置、显式重连 DB::purge('mysql'); Config::set('database.connections.mysql.database', "db_{$dbSlug}"); DB::reconnect('mysql'); // 可选:校验数据库连接有效性 try { DB::connection('mysql')->getPdo(); } catch (\Exception $e) { abort(500, '租户数据库连接失败'); } return $next($request); } }
更优方案:多连接隔离主库与租户库(推荐)
直接修改默认连接容易导致主应用操作串库,建议单独配置租户连接实现完全隔离:
- 打开
config/database.php新增租户连接配置:
'connections' => [ // 原有主库mysql配置保持不变 'tenant' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'strict' => true, // database 留空,中间件动态赋值 ], ]
- 所有租户侧模型(包括客户端用户模型、Sanctum token 模型)指定连接为
tenant:
// 租户模型内添加属性 protected $connection = 'tenant';
- 修改
config/sanctum.php配置,让认证逻辑读取租户库的token:
'storage' => [ 'database' => [ 'connection' => 'tenant', 'table' => 'personal_access_tokens', ], ],
- 调整中间件逻辑,仅修改租户连接配置:
DB::purge('tenant'); Config::set('database.connections.tenant.database', "db_{$dbSlug}"); DB::reconnect('tenant');
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

