hydrat-agency / laravel-2fa
此包允许您在Laravel应用程序中启用双因素认证。它将令牌本地存储,并通过邮件、短信或任何自定义渠道通知用户。包括原生的条件性检查,用于触发或跳过2FA,使用已知设备、IP地址或IP地理位置。
Requires
- php: ^7.3|^7.4|^8.0
README
简介
此包允许您非常容易地在Laravel应用程序中启用双因素认证,无需添加中间件或修改您的路由。它在数据库的独立表中存储令牌,因此您无需修改users
表。通过邮件、短信或任何自定义渠道通知用户关于他们的令牌。
包括原生的条件性检查来触发或跳过2FA:当用户使用已知浏览器、IP地址、IP地理位置或任何自定义规则时,您可以选择跳过检查。
此包受到了srmklive/laravel-twofactor-authentication包的启发,该包支持Authy 2FA认证。
安装
- 使用composer安装包
composer require hydrat-agency/laravel-2fa
- 将服务提供者添加到
config/app.php
文件中的providers
数组中,如下所示
'providers' => [ [...] /* * Package Service Providers... */ Hydrat\Laravel2FA\Laravel2FAServiceProvider::class, ],
- 运行以下命令发布资产
php artisan vendor:publish --provider "Hydrat\Laravel2FA\Laravel2FAServiceProvider"
- 运行以下命令迁移数据库
php artisan migrate
- 在您的用户模型(例如
App\Models\User.php
)中添加以下行
- 在类声明之前添加以下行
use Hydrat\Laravel2FA\TwoFactorAuthenticatable; use Hydrat\Laravel2FA\Contracts\TwoFactorAuthenticatableContract;
- 修改类定义以实现
TwoFactorAuthenticatableContract
协议
class User extends Authenticatable implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract, TwoFactorAuthenticatableContract
- 添加
TwoFactorAuthenticatable
特质
use Authenticatable, Authorizable, CanResetPassword, TwoFactorAuthenticatable;
-
确保您的用户模型使用Notifiable特质。
-
您需要通过在
app\Http\Controllers\Auth\LoginController.php
类中添加authenticated
方法来更改登录工作流程。
<?php namespace App\Http\Controllers\Auth; use Hydrat\Laravel2FA\TwoFactorAuth; class LoginController extends Controller { /** [...] **/ /** * The user has been authenticated. * * @param \Illuminate\Http\Request $request * @param mixed $user * @return mixed */ protected function authenticated(Request $request, $user) { # Trigger 2FA if necessary. if (TwoFactorAuth::getDriver()->mustTrigger($request, $user)) { return TwoFactorAuth::getDriver()->trigger($request, $user); } # If not, do the usual job. return redirect()->intended($this->redirectPath()); }
🚀 如果您喜欢简写版本,也可以使用
/** * The user has been authenticated. * * @param \Illuminate\Http\Request $request * @param mixed $user * @return mixed */ protected function authenticated(Request $request, $user) { return TwoFactorAuth::getDriver()->maybeTrigger($request, $user) ?: redirect()->intended($this->redirectPath()); }
这就完成了!现在,您可能想要自定义视图并查看配置部分。
构建视图
当您发布了包的资产时,将创建一个新的resources/views/auth/2fa/token.blade.php
文件。您可以根据自己的喜好设计此页面,但您必须保留token
表单输入名称,并将表单发送到route('auth.2fa.store')
路由。
您可能注意到一个$reason
变量,它会告诉您为什么触发了2FA认证。根据您的应用程序需求,您可以选择是否向用户显示它。
配置
所有配置都设置在创建包时创建的config/laravel-2fa.php
文件中。
内置
首先,您需要选择哪些策略生效。一个Policy
任务是为了检查是否必须进行双因素认证,或者是否可以跳过(例如:浏览器是否已知?跳过双因素认证)。
策略定义在policy
键中。规则可以组合,并具有优先级顺序。每个策略都会被调用,并告诉驱动器是否应该触发双因素认证。当策略需要双因素认证时,检查停止,并将其返回的message
用作视图中的$reason
(请参阅构建视图部分)。
如果没有策略触发,或者如果policy
数组为空,则跳过双因素认证,并正常登录用户。
return [ 'policy' => [ 'browser', // first check if we know the browser 'geoip', // if so, check if we know the user ip location // if so, no more rules : skip 2FA. ], ];
内置策略是
ℹ️ 需要创建自己的策略吗?请参阅下面的自定义策略部分。
一些策略有额外的设置,这些设置在配置文件中有自我说明。
return [ /* |-------------------------------------------------------------------------- | The 2FA package options. |-------------------------------------------------------------------------- | | Here you may specify the package options, such as policies parameters. | */ 'options' => [ # 2FA token lifetime in minutes. 'token_lifetime' => 10, 'policies' => [ # Can be one of "country", "region", "city", "time_zone". 'geoip' => 'country', # Cookie expiration time in minutes (default 30 days). 'browser' => 30 * 1440, ], ], ];
自定义通知
此包使用laravel的通知系统。内置的TwoFactorToken
通知通过邮件将双因素令牌发送给用户。
您可以通过扩展此类来扩展此通知并配置其他渠道,例如短信
<?php namespace App\Notifications; use Hydrat\Laravel2FA\Notifications\TwoFactorToken as BaseTwoFactorToken; class TwoFactorToken extends BaseTwoFactorToken { /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return [ 'nexmo', ]; } /** * Get the Vonage / SMS representation of the notification. * * @param mixed $notifiable * @return NexmoMessage */ public function toNexmo($notifiable) { return (new NexmoMessage) ->content('Your two-factor token is ' . $this->token) ->from('MYAPP'); } }
您需要更改notification
配置键来指定您的新通知类
return [ [...] /* |-------------------------------------------------------------------------- | The 2FA notification containing the token. |-------------------------------------------------------------------------- | | Here you may specify an alternative notification to use. | */ 'notification' => \App\Notifications\TwoFactorToken::class, ];
自定义策略
如果您对内置策略不满意,可以覆盖现有的策略或创建自己的策略。
所有策略都必须扩展AbstractPolicy
。
要覆盖现有的策略,可以直接扩展策略类
<?php namespace App\Auth\Policies; use Hydrat\Laravel2FA\Policies\IpPolicy as BaseIpPolicy; class IpPolicy extends BaseIpPolicy { /** * Check that the request passes the policy. * If this return false, the 2FA Auth will be triggered. * * @return bool */ public function passes(): bool { # Passes the check if the user didn't activate IpPolicy on his account. if ( ! $this->user->hasTwoFactorAuthActiveForIp()) { return true; } # Else, run the IpPolicy check. return parent::passes(); } /** * The reason sent to the Notification and the frontend view, * to tell the user why the 2FA check was triggered. * * @return string */ public function message(): string { return $this->message ?: __('your account activated 2FA for unknown IP adresses.'); } }
然后,更改设置中的mapping
数组
return [ [...] 'mapping' => [ [...] 'ip' => \Auth\Policies\IpPolicy::class, ], ];
ℹ️ AbstractPolicy有3个可用的属性,您可以在passes()
方法中使用它们来构建策略检查
/** * The incomming request at login. * * @var \Illuminate\Http\Request */ protected $request = null; /** * The user that just loggued in. * * @var \Hydrat\Laravel2FA\Contracts\TwoFactorAuthenticatableContract */ protected $user = null; /** * The login attempt, with UID and IP address data. * * @var \Hydrat\Laravel2FA\Models\LoginAttempt */ protected $attempt = null;
创建策略很简单。例如,假设您的用户可能在设置中激活他们的账户的2FA。您可以创建一个策略来验证用户是否激活了2FA,如果是,则使passes()
方法失败,从而触发2FA认证
<?php namespace App\Auth\Policies; use Hydrat\Laravel2FA\Policies\AbstractPolicy; class ActivePolicy extends AbstractPolicy { /** * Check that the request passes the policy. * If this return false, the 2FA Auth will be triggered. * * @return bool */ public function passes(): bool { return $this->user->hasTwoFactorAuthActive() ? false : true; } /** * The reason sent to the Notification and the frontend view, * to tell the user why the 2FA check was triggered. * * @return string */ public function message(): string { return $this->message ?: __('your account activated the 2FA auth'); } }
您还可以有不同的检查,这些检查会产生不同的$reason
消息
<?php namespace App\Auth\Policies; use Hydrat\Laravel2FA\Policies\AbstractPolicy; class ActivePolicy extends AbstractPolicy { /** * Check that the request passes the policy. * If this return false, the 2FA Auth will be triggered. * * @return bool */ public function passes(): bool { if ($this->user->hasTwoFactorAuthActive()) { $this->message = __('your account activated the 2FA auth'); return false; } if ($this->user->didntSpecifyTwoAuthActive()) { $this->message = __('2FA auth is activated by default'); return false; } if (anyReason()) { return false; // will use the default reason used in message() method. } return true; } /** * The reason sent to the Notification and the frontend view, * to tell the user why the 2FA check was triggered. * * @return string */ public function message(): string { return $this->message ?: __('2FA auth is automatically activated for your account'); } }
创建您的策略后,您可以在配置文件中使用它
return [ 'policy' => [ \Auth\Policies\ActivePolicy::class, ], ];
更好的是,您可以为您的policy
数组创建一个简短名称,以保持其清洁!
return [ 'policy' => [ 'active', // your new rule ! 'browser', // if 2FA is not activated for the account, will check anyways if the browser is known ], [...] 'mapping' => [ [...] 'active' => \Auth\Policies\ActivePolicy::class, ], ];
一些策略需要在用户成功使用2FA完成登录时执行操作(例如:写入cookie或将某些内容写入数据库)。您可以在策略的onSucceed()
方法中定义您的回调
/** * An action to perform on successful 2FA login. * May be used to remember stuff for the next policy check. * * @return void */ public function onSucceed(): void { Cookie::queue( '2fa_remember', $this->attempt->uid, 1440 ); }
自定义驱动程序
如果您在整个过程中需要更多的灵活性,可以扩展BaseDriver
类并覆盖任何方法来更改其工作流程。
namespace App\Auth\Drivers; use Hydrat\Laravel2FA\Drivers\BaseDriver; use Hydrat\Laravel2FA\Contracts\TwoFactorAuthenticatableContract as Authenticatable; class CustomDriver extends BaseDriver { /** * Check if must trigger 2FA token for this user. * * @param \Illuminate\Http\Request $request * @param \Hydrat\Laravel2FA\Contracts\TwoFactorAuthenticatableContract $user * * @return bool */ public function mustTrigger(Request $request, Authenticatable $user): bool { // custom workflow. } }
别忘了更新配置文件中的driver
键
return [ 'driver' => \App\Auth\Drivers\CustomDriver::class; ];
⚠️ 如果您希望从头开始构建驱动程序,您必须实现TwoFactorDriverContract。
贡献
请随时为此包做出贡献!
如果您发现任何安全问题,请通过thomas@hydrat.agency与我联系,而不是创建公开的github问题。
致谢
许可协议
MIT许可协议(MIT)。有关更多信息,请参阅许可文件。