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

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的方案更成熟:

  1. 每个租户对应一个数据库,Tenancy会自动根据子域名切换租户
  2. 在config/auth.php里配置两个守卫:一个主库的web守卫(给超管),一个租户库的tenant-web守卫(给客户用户)
  3. 超管理由用主域名,用web守卫;租户路由用子域名,用tenant-web守卫

这样Tenancy会自动帮你切库,认证逻辑也不用自己写太多。

一些注意事项

  • 数据库连接一定要purge再reconnect,不然会用之前的旧连接,导致切库失败
  • 主库的clients表要存每个客户的数据库地址、用户名、密码等信息,方便动态配置
  • 密码哈希要保持一致,Laravel默认用bcrypt,所以主库和客户库的用户密码哈希算法是一样的,没问题
  • 要做好错误处理,比如客户不存在、数据库连接失败等情况,给用户返回清晰的提示

备注:内容来源于stack exchange,提问作者Mahesh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 08:19:37