返回

Filament 使用外部数据库表进行用户认证终极指南

php

Filament 使用外部数据库表进行用户认证

这问题挺常见,Filament 默认使用 Laravel 的 users 表进行认证。 但很多时候,我们需要用已有的用户表,还可能在不同的数据库里!这篇博客就来手把手教你如何配置 Filament,让它从另一个数据库的指定表读取用户信息进行登录验证。

问题根源

核心在于两点:一是 Laravel 的认证机制默认配置;二是 Filament 默认继承并使用了 Laravel 的这套认证体系。

  • Laravel 认证配置: Laravel 默认的认证守卫(guard)和用户提供者(provider)配置通常指向本地数据库的 users 表。 相关的配置项位于 config/auth.php 文件。
  • Filament 用户模型: Filament 安装时会生成一个 User 模型(通常在 app/Models 下),这个模型也默认与 users 表关联。

解决方案:三步走

要让 Filament 从外部数据库的 usr 表认证,主要就三步:配置数据库连接、修改认证配置、调整 Filament 的用户模型。

1. 配置外部数据库连接

首先,得让 Laravel 能连上那个存着 usr 表的数据库。

打开 .env 文件,添加新的数据库连接信息。 假设外部数据库是 MySQL,那么像下面这样配置:

DB_CONNECTION_EXTERNAL=mysql
DB_HOST_EXTERNAL=192.168.0.31
DB_PORT_EXTERNAL=3306
DB_DATABASE_EXTERNAL=dbusers
DB_USERNAME_EXTERNAL=dbadmin
DB_PASSWORD_EXTERNAL=dbadmin@123

然后在 config/database.php 里,connections 数组中增加对应的连接配置:

'connections' => [

    // ... 其他数据库配置 ...

    'mysql_external' => [
        'driver' => 'mysql',
        'host' => env('DB_HOST_EXTERNAL', '127.0.0.1'),
        'port' => env('DB_PORT_EXTERNAL', '3306'),
        'database' => env('DB_DATABASE_EXTERNAL', 'forge'),
        'username' => env('DB_USERNAME_EXTERNAL', 'forge'),
        'password' => env('DB_PASSWORD_EXTERNAL', ''),
        'unix_socket' => env('DB_SOCKET', ''),
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
        'prefix' => '',
        'prefix_indexes' => true,
        'strict' => true,
        'engine' => null,
        'options' => extension_loaded('pdo_mysql') ? array_filter([
            PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
        ]) : [],
    ],
],

注意 :
* .env 中的配置项,其 DB_CONNECTION_EXTERNAL 是和这里数组的键 mysql_external 关联的, 自己要命名得对应上。
* 确保 MySQL 用户 dbadmin 具有从 Laravel 应用所在服务器访问 dbusers 数据库的权限。

2. 修改 Laravel 认证配置

接下来,修改 config/auth.php 文件, 让 Laravel 认证使用刚刚配置好的外部数据库和 usr 表。

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'external_users', // 改这里,指向新的 provider
    ],

    // ... Filament 可能还有其他 guard 配置,按需修改 ...
],

'providers' => [
    'users' => [ //通常是 users
        'driver' => 'eloquent',
        'model' => App\Models\User::class, // 注意: 这行通常注释掉, 因为我们要重新定向provider
    ],

    'external_users' => [ // 新增一个 provider
        'driver' => 'database',
        'table' => 'usr', // 外部数据库的表名
        'connection' => 'mysql_external'// 指向第一步的数据库连接
    ],
],

解读:

  • guards.web.providerweb 这个守卫现在使用名为 external_users 的用户提供者。
  • providers.external_users: 这个自定义的提供者指定了:
    • driver:使用 database 驱动 (直接查询数据库)。
    • table:使用 usr 表。
    • connection: 使用'mysql_external' 连接

3. 调整 Filament 的 User 模型

有两种方法可以适配Filament用户模型,根据具体情况选用:

方法一: 重写 getUserByAuthIdentifier (更推荐):

如果不想完全改变Filament获取用户的方式,只希望它使用不同的字段进行用户识别,可以在App\Models\User 模型(或者你自定义的与 Filament 用户相关的模型) 中重写 getUserByAuthIdentifier 方法:

// 在 app/Models/User.php (或者你自定义的用户模型) 中
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    // ... 其他代码 ...
      protected $table = 'users'; //注意要根据实际修改

    public function getUserByAuthIdentifier($identifier)
    {

        //  从外部数据中,使用给定的 userId  查找
         return \DB::connection('mysql_external')->table('usr')->where('userId', $identifier)->first();
    }

   public function getAuthPassword()
   {
     //   'userPassword' 是密码的实际字段
        return \DB::connection('mysql_external')->table('usr')->where('userId', $this->getAuthIdentifier())->value('userPassword');
   }

     /**
      * 重新定义getAuthIdentifierName, 返回外部表的段。
      * @return string
      */
    public function getAuthIdentifierName()
   {
     return "userId";
   }
      //  其他的自定义逻辑和方法可以添加到这里
}

方法二:新建 User Model (彻底换模型):

如果你希望 完全usr 表替换原来的 users 表,可以新建一个 User 模型:

  1. 创建新模型:

    php artisan make:model ExternalUser
    
  2. 编辑 ExternalUser 模型 (app/Models/ExternalUser.php):

    <?php
    
    namespace App\Models;
    
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable; // 如果需要通知功能
    
    class ExternalUser extends Authenticatable
    {
        use Notifiable;
    
        protected $connection = 'mysql_external'; // 使用外部数据库连接
        protected $table = 'usr'; // 表名
        protected $primaryKey = 'userId';   //主键名
        public $incrementing = false; // 如果 userId 不是自增的
        protected $keyType = 'string';//如果 userId 是字符串
    
        protected $fillable = [
            'userId', 'email', 'userPassword', // 允许 mass assignment 的字段
        ];
    
        protected $hidden = [
            'userPassword', // 隐藏密码字段
        ];
    
      //  重写获取密码的方法
      public function getAuthPassword()
        {
             return $this->userPassword;
        }
         /**
      * 重新定义getAuthIdentifierName, 返回外部表的关键字段。
      * @return string
      */
       public function getAuthIdentifierName()
        {
          return "userId";
        }
    }
    
    
  3. 修改 Filament 配置:

    打开 config/filament.php (或者 config/filament-panel.php,取决于你的 Filament 版本),找到 auth.user 或者 类似的配置项,改成:
    注意:对于不同的版本可能存在差别,需要根据你所用的版本具体情况修改。

        'auth' => [
            // ...
           'user' => App\Models\ExternalUser::class,  //改成新的model
           // ...
         ]
    

安全建议:

  • 密码哈希: 绝对不要在数据库中明文存储密码! 确保 usr 表中的 userPassword 字段存储的是哈希过的密码。 如果原系统没有哈希,强烈建议在迁移或同步数据时进行哈希处理(例如使用 Laravel 的 Hash::make())。
  • 最小权限原则: 数据库用户 dbadmin 只需要对 dbusers 数据库的 usr 表有必要的 SELECT 权限就够了,不要给多余的权限。
  • 数据库连接安全: 将数据库的配置写在.env文件中,然后把.env文件添加到.gitignore, 不提交到版本控制里。

进阶使用技巧:

如果需要登录时使用email做唯一校验,而不是userId。 可以重写用户模型中的几个方法:
getAuthIdentifierName 改成return 'email';。 然后, 再重写findForPassport方法。

 /**
     * Passport 相关方法, 通过email寻找user
     * @param $username
     * @return mixed
     */
public function findForPassport($username) {
     return $this->where('email', $username)->first();
}