Laravel多数据库架构下的分库认证实现咨询
Laravel多数据库架构下的分库认证实现咨询
嘿,我来帮你捋捋这个多库认证的问题!你说的这种SaaS场景我之前也接触过,核心就是在用户登录时先判断身份,再切换到对应数据库做认证,Laravel本身的Auth系统是可以扩展实现的,不一定非要依赖Tenancy(当然Tenancy配置对了也能搞定,后面我也提一嘴)。
咱们一步步来拆解实现思路:
一、先明确登录时的身份识别逻辑
首先得搞清楚:怎么区分用户是超级管理员还是客户用户?常见的两种方式:
- 让用户在登录表单里选择身份(比如加个「超级管理员登录」的复选框,或者下拉选择「管理员/客户用户」)
- 通过用户输入的信息自动判断(比如超级管理员的邮箱固定是
admin@your-saas.com这类特定域名,或者客户用户需要输入专属的「客户编码」)
我这里以「用户选择身份+客户编码」的场景为例,给你写具体代码。
二、重写登录控制器的认证逻辑
默认的LoginController是用主库做认证的,咱们需要重写attemptLogin方法,在认证前先切换到对应数据库:
protected function attemptLogin(Request $request) { // 1. 判断是否是超级管理员登录 if ($request->filled('is_super_admin')) { // 切换回主数据库(假设主库连接名是mysql) config(['database.default' => 'mysql']); DB::purge('mysql'); // 清除旧连接缓存 DB::reconnect('mysql'); // 重新连接 } else { // 2. 客户用户登录:先从主库获取客户的数据库配置 $clientCode = $request->input('client_code'); $client = DB::connection('mysql')->table('clients')->where('code', $clientCode)->first(); if (!$client) { $this->addError('client_code', '该客户不存在'); return false; } // 3. 动态配置客户数据库连接 config(['database.connections.client' => [ 'driver' => 'mysql', 'host' => $client->db_host, 'database' => $client->db_name, 'username' => $client->db_user, 'password' => $client->db_password, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, ]]); // 4. 设置默认连接为客户库,并重连 config(['database.default' => 'client']); DB::purge('client'); DB::reconnect('client'); } // 5. 调用Laravel默认的认证逻辑,此时已经用的是对应数据库了 return $this->guard()->attempt( $this->credentials($request), $request->filled('remember') ); }
三、登录后保存身份信息,后续请求自动切库
用户登录成功后,得把身份类型(超管/客户用户)和客户编码存在session里,这样后续每个请求都能自动切换到对应数据库。咱们重写authenticated方法:
protected function authenticated(Request $request, $user) { if ($request->filled('is_super_admin')) { session(['user_type' => 'super_admin']); } else { session(['user_type' => 'client_user', 'client_code' => $request->input('client_code')]); } }
然后创建一个中间件SwitchDatabase,用来在每个请求前切换数据库:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Auth; class SwitchDatabase { public function handle($request, Closure $next) { if (Auth::check()) { $userType = session('user_type'); if ($userType === 'super_admin') { // 切回主库 config(['database.default' => 'mysql']); DB::purge('mysql'); DB::reconnect('mysql'); } elseif ($userType === 'client_user') { // 从主库拿客户配置,切到客户库 $clientCode = session('client_code'); $client = DB::connection('mysql')->table('clients')->where('code', $clientCode)->first(); if ($client) { config(['database.connections.client' => [ 'driver' => 'mysql', 'host' => $client->db_host, 'database' => $client->db_name, 'username' => $client->db_user, 'password' => $client->db_password, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, ]]); config(['database.default' => 'client']); DB::purge('client'); DB::reconnect('client'); } } } return $next($request); } }
最后把这个中间件加到app/Http/Kernel.php的web中间件组里,这样所有web请求都会自动切库。
四、关于Laravel Tenancy的补充
你之前说用Tenancy只实现了CRUD,其实它是支持认证的,可能是你没配置对。如果你的SaaS是用子域名区分客户的,Tenancy的方案更成熟:
- 每个租户对应一个数据库,Tenancy会自动根据子域名切换租户
- 在
config/auth.php里配置两个守卫:一个主库的web守卫(给超管),一个租户库的tenant-web守卫(给客户用户) - 超管理由用主域名,用
web守卫;租户路由用子域名,用tenant-web守卫
这样Tenancy会自动帮你切库,认证逻辑也不用自己写太多。
一些注意事项
- 数据库连接一定要
purge再reconnect,不然会用之前的旧连接,导致切库失败 - 主库的
clients表要存每个客户的数据库地址、用户名、密码等信息,方便动态配置 - 密码哈希要保持一致,Laravel默认用bcrypt,所以主库和客户库的用户密码哈希算法是一样的,没问题
- 要做好错误处理,比如客户不存在、数据库连接失败等情况,给用户返回清晰的提示
备注:内容来源于stack exchange,提问作者Mahesh
相关产品推荐
相关产品推荐

