laravel 8 同时启用双因素认证和邮箱验证,需user模型同时实现mustverifyemail接口并使用twofactorauthenticatable trait,补全email_verified_at及two_factor_secret、recovery_codes、confirmed_at三字段,配置fortify的两项features,并手动拦截登录流程强制2fa验证。

要在 Laravel 8 中同时启用双因素认证(2FA)和邮箱验证功能,必须分别处理两个独立但可共存的认证增强模块:Fortify 提供 2FA 后端逻辑与邮箱验证路由注册能力,但【User 模型必须同时实现 MustVerifyEmail 接口并使用 TwoFactorAuthenticatable trait】,缺一不可,否则任一功能将无法触发或静默失效。
准备用户模型与数据库字段
第一步:打开 app/Models/User.php,在类声明后添加两行:
use Illuminate\Contracts\Auth\MustVerifyEmail;use Laravel\Fortify\TwoFactorAuthenticatable;
第二步:修改 User 类声明,补全两个接口/ trait:
class User extends Authenticatable implements MustVerifyEmail{ use TwoFactorAuthenticatable; // ...其余代码}
第三步:运行迁移命令补全三类字段——邮箱验证只需 email_verified_at(Laravel 8 默认已存在),但 2FA 必须新增三个字段:【two_factor_secret、two_factor_recovery_codes、two_factor_confirmed_at】。执行:
php artisan make:migration add_two_factor_columns_to_users_table
编辑生成的迁移文件,在 up() 方法中写入:
$table->string('two_factor_secret')->nullable();$table->text('two_factor_recovery_codes')->nullable();$table->timestamp('two_factor_confirmed_at')->nullable();
最后执行 php artisan migrate。跳过此步会导致启用 2FA 时直接抛出 Column not found 错误。
启用 Fortify 的双因素与邮箱验证功能
打开 config/fortify.php,找到 'features' 数组,确保包含:
Features::twoFactorAuthentication(),Features::emailVerification(),
注意:仅开启配置项不会自动拦截登录流程——2FA 需手动干预,而邮箱验证会自动生效(只要 User 实现了 MustVerifyEmail)。
运行 php artisan fortify:install 发布配置与服务提供者(若尚未执行)。该命令会注册 FortifyServiceProvider 并发布默认配置。
强制登录后检查邮箱验证状态
Laravel 8 原生支持邮箱验证中间件,无需额外编码即可拦截未验证用户:
在 routes/web.php 中,对需保护的路由添加 verified 中间件:
Route::get('/dashboard', function () { return view('dashboard');})->middleware(['auth', 'verified']);
用户首次注册后,系统自动发送验证邮件;点击邮件链接即设置 email_verified_at 时间戳,中间件放行。
拦截登录流程以强制完成 2FA
Fortify 默认不阻断已通过密码验证的用户,必须手动注入检查逻辑:
编辑 app/Providers/FortifyServiceProvider.php,在 boot() 方法内添加:
Fortify::authenticateUsing(function (Request $request) { $user = User::where('email', $request->email)->first(); if ($user && Hash::check($request->password, $user->password) && $user->two_factor_secret && !$request->session()->has('two-factor-authenticated')) { return redirect('/two-factor-challenge'); } return $user;});
这一步覆盖了默认认证行为:当用户有 2FA 密钥且尚未通过挑战时,直接重定向至 /two-factor-challenge 页面,而不是登录成功。
确保前端在该页面提交验证码时,请求地址为 POST /two-factor-challenge,并携带 code 字段——Fortify 内置控制器会处理验证与 session 标记。
修复 Livewire 中 QR 码空白问题
方法一:在 Blade 模板中调用模型方法生成 URL
不要写 {{ decrypt($user->two_factor_secret) }}——这在 Livewire 上下文中大概率失败。
改用:{{ $user->getTwoFactorSecretQrCodeUrl() }}
该方法内部自动解密密钥、构造 otpauth:// URL 并做 URL 编码,兼容性更强。
方法二:确认 two_factor_confirmed_at 为 NULL
只有当该字段为 NULL 时,getTwoFactorSecretQrCodeUrl() 才返回有效 URL;若已被设为时间戳,说明已确认,QR 码不再显示。
开发调试时可临时清空该字段:DB::table('users')->where('id', 1)->update(['two_factor_confirmed_at' => null]);











