Languages

Version

Theme

用户

多因素认证

介绍

Filament 用户默认可以使用邮箱和密码登录。不过,你可以启用多因素认证(MFA),为用户提供额外的安全保障。

启用MFA后,用户在通过身份验证并访问应用之前必须执行额外的验证。

Filament 包括两种可以开箱即用的 MFA 方法:

  • 应用身份认证使用与 Google 身份验证器兼容的应用(如 Google 身份验证器、Authy 或 Microsoft 身份验证器应用)生成用于验证用户的基于时间的一次性密码(TOTP)。
  • 电子邮件身份认证向用户的电子邮件地址发送一个一次性代码,用户必须输入该代码以验证其身份。

在 Filament 中,用户在他们的个人资料(profile)页面设置多因素身份验证。如果你使用 Filament 的个人资料页面功能,设置多因素身份验证将自动将对应的 UI 元素添加到个人资料页面:

use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->profile();
}

应用身份认证

要在面板中启用应用身份验证,你必须首先向 users 表(或此面板中“可身份验证(authenticatable)” 的 Eloquent 模型的任何表)添加一个新字段。该字段需要存储用于生成和验证基于时间的一次性密码的密钥。它可以是迁移中的普通 text() 字段:

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->text('app_authentication_secret')->nullable();
});

User 模型中,你需要确保该字段是加密的且 $hidden,因这是十分敏感的信息,应该保证存储安全:

use Filament\Models\Contracts\FilamentUser;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements FilamentUser, MustVerifyEmail
{
    // ...

    /**
     * @var array<string>
     */
    protected $hidden = [
        // ...
        'app_authentication_secret',
    ];

    /**
     * @var array<string, string>
     */
    protected $casts = [
        // ...
        'app_authentication_secret' => 'encrypted',
    ];
    
    // ...
}

然后,你需要在 User 模型中实现 HasAppAuthentication 接口。该接口提供了与密钥及其他集成信息进行交互的必要方法:

use Filament\Auth\MultiFactor\App\Contracts\HasAppAuthentication;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements FilamentUser, HasAppAuthentication, MustVerifyEmail
{
    // ...

    public function getAppAuthenticationSecret(): ?string
    {
        // This method should return the user's saved app authentication secret.
    
        return $this->app_authentication_secret;
    }

    public function saveAppAuthenticationSecret(?string $secret): void
    {
        // This method should save the user's app authentication secret.
    
        $this->app_authentication_secret = $secret;
        $this->save();
    }

    public function getAppAuthenticationHolderName(): string
    {
        // In a user's authentication app, each account can be represented by a "holder name".
        // If the user has multiple accounts in your app, it might be a good idea to use
        // their email address as then they are still uniquely identifiable.
    
        return $this->email;
    }
}

TIP

由于 Filament 在 User 模型上实现了接口,而非假定 app_authentication_secret 字段存在,你可以使用任何想要使用的字段名。如果你的 密钥存储在不同的表格种,你甚至可以使用完全不同的模型。

最后,你应该在面板中激活应用身份认证功能。为此,请在配置中使用 multiFactorAuthentication() 方法,并传入一个 AppAuthentication 实例:

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            AppAuthentication::make(),
        ]);
}

设置应用恢复码

如果用户丢失访问他们的双因素身份验证应用的权限,他们将无法登录到应用中。为了防止这种情况,你可以生成一组恢复码,用户可以在失去对双因素身份验证应用的访问权限时使用这些代码登录。

app_authentication_secret 字段类似, 你应该向 users表(或面板中使用 “authenticatable” Eloquent 模型的任何表)添加一个新字段。该字段用于存储恢复码。它可以是迁移中的普通 text() 字段:

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->text('app_authentication_recovery_codes')->nullable();
});

User 模型中,你需要该字段加密成数组且为 $hidden,因为它是敏感信息,应该保证存储安全:

use Filament\Auth\MultiFactor\App\Contracts\HasAppAuthentication;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements FilamentUser, HasAppAuthentication, MustVerifyEmail
{
    // ...

    /**
     * @var array<string>
     */
    protected $hidden = [
        // ...
        'app_authentication_recovery_codes',
    ];

    /**
     * @var array<string, string>
     */
    protected $casts = [
        // ...
        'app_authentication_recovery_codes' => 'encrypted:array',
    ];
    
    // ...
}

接下来,你需要在 User 模型中实现 HasAppAuthenticationRecovery 接口。该接口提供了与恢复码进行交互的必要方法:

use Filament\Auth\MultiFactor\App\Contracts\HasAppAuthentication;
use Filament\Auth\MultiFactor\App\Contracts\HasAppAuthenticationRecovery;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements FilamentUser, HasAppAuthentication, HasAppAuthenticationRecovery, MustVerifyEmail
{
    // ...

    /**
     * @return ?array<string>
     */
    public function getAppAuthenticationRecoveryCodes(): ?array
    {
        // This method should return the user's saved app authentication recovery codes.
    
        return $this->app_authentication_recovery_codes;
    }

    /**
     * @param  array<string> | null  $codes
     */
    public function saveAppAuthenticationRecoveryCodes(?array $codes): void
    {
        // This method should save the user's app authentication recovery codes.
    
        $this->app_authentication_recovery_codes = $codes;
        $this->save();
    }
}

TIP

由于 Filament 在 User 模型上使用了接口,而不是假定存在 app_authentication_recovery_codes 字段,因此你可以使用任何你想要的字段名。如果你的恢复码保存在不同的表格中,你甚至可以使用完全不同的模型。

最后,你应该在面板中激活应用身份验证恢复代码功能。为此,请在配置中的 multiFactorAuthentication() 方法中 将 recovery() 方法传递给 AppAuthentication 实例:

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            AppAuthentication::make()
                ->recoverable(),
        ]);
}

修改恢复码生成的数量

默认情况下,Filament 为每位用户生成 8 个恢复码。如果你想修改,可以在配置multiFactorAuthentication() 方法中,使用 AppAuthentication 实例调用 recoveryCodeCount() 方法:

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            AppAuthentication::make()
                ->recoverable()
                ->recoveryCodeCount(10),
        ]);
}

防止用户重新生成恢复码

默认情况下,用户可以访问它们的个人资料页来重新生成恢复码。如果你想要阻止重新生成,可以在配置multiFactorAuthentication() 方法中,使用 AppAuthentication 实例调用 regenerableRecoveryCodes(false) 方法:

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            AppAuthentication::make()
                ->recoverable()
                ->regenerableRecoveryCodes(false),
        ]);
}

修改应用码到期时间

应用码是使用基于时间的一次性密码(TOTP)算法发布的,这意味着它们在生成前后仅在短时间内有效。该时间是在时间“窗口”中定义的。默认情况下, Filament 使用 8 的过期窗口,即在生成时间的两侧创建 4 分钟的有效期(总共 8 分钟)。

要更改窗口,例如使其在生成后仅在 2 分钟内有效,你可以在 AppAuthentication 实例上使用 codeWindow()方法,将其设置为 4

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            AppAuthentication::make()
                ->codeWindow(4),
        ]);
}

自定义应用认证的品牌名

每个应用认证集成都有一个展示在认证应用上的“品牌名”。默认情况下,它是应用名。如果你想修改品牌名,你可以在配置multiFactorAuthentication() 方法的 AppAuthentication 实例上调用 brandName() 方法:

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            AppAuthentication::make()
                ->brandName('Filament Demo'),
        ]);
}

电子邮件身份认证

邮箱认证将会发送一次性验证码到用户邮箱地址中,用户必须输入该验证码进行确认。

要在面板中启用邮箱认证,你必须先新增一个用以验证的列到 users 表(或者其他使用 authenticatable 的 Eloquent 模型的表)中。该列用以存储布尔值,说明邮箱认证是否激活。

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->boolean('has_email_authentication')->default(false);
});

User 模型中,你需要确保该字段被强制转换(cast)为布尔值:

use Filament\Models\Contracts\FilamentUser;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements FilamentUser, MustVerifyEmail
{
    // ...
    /**
     * @var array<string, string>
     */
    protected $casts = [
        // ...
        'has_email_authentication' => 'boolean',
    ];
    
    // ...
}

然后,在 User 模型中实现 HasEmailAuthentication 接口。这提供给 Filament 必须的方法,用来与说明邮箱认证是否激活的字段进行交互。

use Filament\Auth\MultiFactor\Email\Contracts\HasEmailAuthentication;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements FilamentUser, HasEmailAuthentication, MustVerifyEmail
{
    // ...

    public function hasEmailAuthentication(): bool
    {
        // This method should return true if the user has enabled email authentication.
        
        return $this->has_email_authentication;
    }

    public function toggleEmailAuthentication(bool $condition): void
    {
        // This method should save whether or not the user has enabled email authentication.
    
        $this->has_email_authentication = $condition;
        $this->save();
    }
}

TIP

由于 Filament 在 User 模型上使用接口,而非假设 has_email_authentication 字段存在,你可以使用任何字段名。你甚至可以使用完全不同的模型,如果你像将设置保存在其他表格中。

最后,你需要在面板中激活邮箱认证功能。为此,请在配置中使用 multiFactorAuthentication() 方法,并传入一个 EmailAuthentication 实例:

use Filament\Auth\MultiFactor\Email\EmailAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            EmailAuthentication::make(),
        ]);
}

修改邮箱验证码到期时间

邮件验证码有 4 分钟的到期时间。

要修改到期时间,比如在生成后的 2 分钟内有效,你可以在 EmailAuthentication 实例的 codeExpiryMinutes() 方法中将其设置为 2

use Filament\Auth\MultiFactor\Email\EmailAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            EmailAuthentication::make()
                ->codeExpiryMinutes(2),
        ]);
}

要求多因素认证

默认情况下,用户没被要求设置多因素认证。你可以将 isRequired: true 作为参数传给配置multiFactorAuthentication() 方法来要求用户设置多因素认证:

use Filament\Auth\MultiFactor\App\AppAuthentication;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->multiFactorAuthentication([
            AppAuthentication::make(),
        ], isRequired: true);
}

启用后,入宫用户还没有启用多因素认证,j将会在登录后被提示设置多因素认证。

关于多因素认证的安全提示

在 Filament 中,多因素认证处理发生在用户实际认证之前。这确保用户不能在未进行多因素认证就访问应用。你无需添加中间件到任何认证路由来确保用户完成多因素认证步骤。

不过,如果你的 Laravel 应用的其他部分也在认证用户,请记住:如果他们已经在其他地方进行过认证,不会再受多因素认证限制,除非多因素认证是必需的并且他们还未进行过设置。

Edit on GitHub

Still need help? Join our Discord community or open a GitHub discussion

Previous
概述