如何在Laravel Filament后台登录中使用自定义SMS接口实现2FA认证
如何在Laravel Filament后台登录中使用自定义SMS接口实现2FA认证
嘿,这问题我熟!要在Laravel Filament后台登录里用上你自己的SMS接口做2FA认证,咱们可以通过自定义登录流程来实现,一步步来:
第一步:确保你的SMS发送Trait能正常工作
你已经写好了OTPmessageTrait,先别急着集成,先单独测试下这个Trait的sendOtp方法能不能正常发短信。比如在Artisan Tinker里跑个测试命令:
php artisan tinker
然后执行:
use App\Models\User; User::first()->sendOtp('96654XXXXXX', '测试验证码:123456');
如果能成功收到短信,说明这个Trait没问题,可以继续下一步。另外记得把用户模型和这个Trait关联上,还要确保用户表有phone字段存手机号:
// app/Models/User.php use App\OTPmessageTrait; class User extends Authenticatable { use OTPmessageTrait; // ...其他原有代码 // 封装一个获取手机号的方法,方便后续调用 public function getPhoneForOtp() { return $this->phone; // 这里的phone是你用户表存手机号的字段,要符合SMS接口的格式要求 } }
第二步:生成自定义Filament登录页面
Filament允许我们替换默认的登录页面来添加自定义逻辑,先跑命令生成自定义登录页:
php artisan make:filament-page Login --section=Auth
生成的文件在app/Filament/Pages/Auth/Login.php,接下来我们要修改这个文件,把登录流程改成「邮箱密码验证→发送OTP→验证OTP→登录成功」的三步逻辑。
第三步:修改自定义登录页面的逻辑
打开生成的Login.php,替换成下面的代码,我都加了注释,你可以跟着调整:
// app/Filament/Pages/Auth/Login.php namespace App\Filament\Pages\Auth; use Filament\Pages\Auth\Login as BaseLogin; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; use Filament\Notifications\Notification; use Illuminate\Support\Facades\Hash; use App\Models\User; class Login extends BaseLogin { // 存储用户输入的OTP和临时用户ID public $otp = ''; public $showOtpForm = false; public $pendingUserId = null; // 页面加载时判断是否需要直接显示OTP输入框 public function mount(): void { parent::mount(); if (session()->has('pending_otp_user')) { $this->pendingUserId = session()->get('pending_otp_user'); $this->showOtpForm = true; } } // 重写核心的登录验证逻辑 public function authenticate() { // 如果当前显示的是OTP输入框,就走OTP验证流程 if ($this->showOtpForm) { return $this->verifyOtpCode(); } // 否则先验证邮箱和密码 $formData = $this->form->getState(); $user = User::where('email', $formData['email'])->first(); // 验证邮箱密码是否正确 if (!$user || !Hash::check($formData['password'], $user->password)) { Notification::make() ->title('邮箱或密码不对哦') ->danger() ->send(); return; } // 生成6位数字OTP,你也可以调整长度 $otpCode = Str::random(6, '0123456789'); // 把OTP存到缓存里,15分钟过期 $cacheKey = "user_{$user->id}_otp"; Cache::put($cacheKey, $otpCode, now()->addMinutes(15)); // 调用你的SMS接口发送验证码 try { $user->sendOtp($user->getPhoneForOtp(), "你的Filament后台登录验证码是:{$otpCode},15分钟内有效,别告诉别人哦!"); } catch (\Exception $e) { Notification::make() ->title('验证码发送失败了,稍后再试试?') ->danger() ->send(); return; } // 把用户ID存到会话,标记为待验证OTP状态 session()->put('pending_otp_user', $user->id); $this->pendingUserId = $user->id; $this->showOtpForm = true; } // 单独写OTP验证的逻辑 protected function verifyOtpCode() { $cacheKey = "user_{$this->pendingUserId}_otp"; $storedOtp = Cache::get($cacheKey); // 验证OTP是否正确或过期 if (!$storedOtp || $storedOtp !== $this->otp) { Notification::make() ->title('验证码不对或者过期啦') ->danger() ->send(); return; } // 验证通过,登录用户 $user = User::findOrFail($this->pendingUserId); auth()->login($user, $this->form->getState('remember')); // 清理缓存和会话里的临时数据 Cache::forget($cacheKey); session()->forget('pending_otp_user'); // 跳转到用户原本要去的页面 $this->redirectIntended($this->getRedirectUrl()); } // 根据当前状态切换表单字段 protected function getFormSchema(): array { if ($this->showOtpForm) { return [ \Filament\Forms\Components\TextInput::make('otp') ->label('短信验证码') ->required() ->length(6) ->numeric() ->autofocus(), ]; } // 不是OTP状态就用默认的邮箱密码表单 return parent::getFormSchema(); } // 根据当前状态切换按钮文字 protected function getSubmitButtonLabel(): string { return $this->showOtpForm ? '验证验证码并登录' : '获取验证码'; } }
第四步:告诉Filament用咱们的自定义登录页
打开config/filament.php配置文件,找到pages配置项,把默认的登录页替换成咱们刚写的:
// config/filament.php 'pages' => [ 'login' => \App\Filament\Pages\Auth\Login::class, ],
一些要注意的细节
- 确保用户表的
phone字段格式和你的SMS接口要求一致(比如要国际号段,不带+号或者带+号,得和你Trait里的dests参数格式匹配) - 可以调整OTP的过期时间,比如改成10分钟,或者限制用户请求OTP的次数防止滥用
- 如果用户多次输错OTP,可以考虑临时锁定用户一段时间,提升安全性
- 记得在
.env里填好你的SMS接口配置:SMS_API_USERNAME、SMS_API_TOKEN、SMS_API_SENDER_ID,还要确保这些配置能被Trait正确读取
这样一套下来,用户登录Filament后台就需要先输邮箱密码,然后收短信验证码,验证通过后才能进入后台,完美用上你自己的SMS接口做2FA啦!
备注:内容来源于stack exchange,提问作者Pierre Sameh
相关产品推荐
相关产品推荐

