转载自 Laravel 论坛: https://learnku.com/laravel/t...
Laravel拥有一个很棒的现成的用户认证系统,固然咱们也须要在在某些地方自定义一些配置。对于某些自定义配置,咱们并不须要再去寻找一个扩展包或者写一大堆代码。让咱们来研究一下这套认证系统背后隐藏着哪些有趣的功能。php
咱们应该都知道方法 Auth::routes()
来自于 Laravel UI package (在Laravel 7以前, 它被包含在内核中)。前端
但你知道它能够接受一个数组来启用/禁用特定的认证路由吗?vue
对于Laravel 7,下面是可用的参数及其默认值:laravel
Auth::routes([ 'login' => true, 'logout' => true, 'register' => true, 'reset' => true, // 用于重置密码 'confirm' => false, // 用于额外的密码确认 'verify' => false, // 用于邮箱认证 ]);
这些参数仅启用或禁用某些路由。git
要了解它们是如何工做的,能够查看文件Laravel UI中的AuthRouteMethods:github
return function ($options = []) { // 登陆路由... if ($options['login'] ?? true) { $this->get('login', 'Auth\LoginController@showLoginForm')->name('login'); $this->post('login', 'Auth\LoginController@login'); } // 登出路由... if ($options['logout'] ?? true) { $this->post('logout', 'Auth\LoginController@logout')->name('logout'); } // 注册路由... if ($options['register'] ?? true) { $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register'); $this->post('register', 'Auth\RegisterController@register'); } // 密码重设路由... if ($options['reset'] ?? true) { $this->resetPassword(); } // 密码确认路由... if ($options['confirm'] ?? class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) { $this->confirmPassword(); } // 邮箱验证路由... if ($options['verify'] ?? false) { $this->emailVerification(); } };
官方文档指定了使用 Laravel UI 的主要方法:web
php artisan ui vue --auth
可是,若是您不须要可视 UI,该怎么办?若是您建立的是一个仅基于 API 的项目,且在框架中没有任何前端呢?数据库
您仍然可使用 Laravel Auth 及其控制器。安装 Laravel UI 并运行如下命令:数组
php artisan ui:controllers
它只会生成 app/Http/Controllers/Auth
, 所以您不须要 Blade 或 Vue 文件便可使用它们。浏览器
请在 Github 存储库 中参阅这个 Artisan 命令的实现。
您是否曾经维护过 Github 存储库,并试图更改其访问设置?而后,Github 要求您再次输入密码,以确保确实是您在操做。
从 Laravel 6.2 开始,框架中也集成了该功能。
您只须要向要保护的路由添加一个名为password.confirm
的中间件便可。
Route::get('/secrets', 'SecretsController@show')->middleware('password.confirm');
Dries Vints 引用自官方功能发布文章:
若是尝试访问该路由,将提示您确认密码,和在 GitHub 等其余应用程序上看到的同样。确认密码后,默认状况下会在用户会话中存储一个时间戳。时间戳持续3个小时,所以用户在此期间没必要再次输入密码。
您可使用
auth
配置文件中的password_timeout
配置选项来自定义此持续时间。
从 Laravel 5.6 起,咱们提供了一种单独的方法来自动注销使用咱们的账户登陆的任何其余设备或浏览器:
Auth::logoutOtherDevices($password);
典型的用法是在当前设备成功登陆后注销其余设备。为此,咱们从 TraitAuthenticatesUsers.php
中重写方法authenticated()
,并将其放入app / Http / Controllers / Auth / LoginController.php
中:
protected function authenticated(Request $request, $user) { \Auth::logoutOtherDevices(request('password')); }
另外,不要忘记激活app / Http / Kernel.php
文件中的中间件AuthenticateSession
,默认状况下该中间件已被注释:
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ],
默认状况下,Laravel 的 LoginController 和 RegisterController 具备相同的属性:
class RegisterController extends Controller { protected $redirectTo = RouteServiceProvider::HOME;
所以,您能够指定成功登陆/注册后重定向到的 URL。默认值在 app/Providers/RouteServiceProvider.php
中:
class RouteServiceProvider extends ServiceProvider { public const HOME = '/home';
如何自定义它?
首先,您能够分别为登陆和注册控制器的$redirectTo
属性指定其余值。
可是,若是您具备更复杂的动态重定向逻辑,例如须要根据用户角色来判断呢?
您能够在身份验证控制器中建立一个redirectTo()
方法,而后在其中指定条件。该方法将覆盖$ redirectTo
属性的任何值。
参见示例:
class RegisterController extends Controller { protected $redirectTo = RouteServiceProvider::HOME; protected function redirectTo() { if (auth()->user()->role_id == 1) { return '/admin'; } return '/home'; }
若是您须要建立一个新用户,但尚未准备好注册页面该怎么办?
只需在您的终端中打开 Laravel Tinker:
php artisan tinker
若是您不熟悉 Tinker,须要知道它是可以执行任何 Laravel / PHP 代码的命令行工具。所以,在其中,您能够轻松建立用户,键入此 Eloquent 命令并按 Enter:
\App\User::create(['name' => 'Admin', 'email' => 'admin@admin.com', 'password' => bcrypt('somesecurepassword')]);
可是,若是您须要建立许多用户进行测试,例如十、100或1000,该怎么办?没问题,咱们能够在database / factories / UserFactory.php
中使用 Laravel 默认提供的 Factory 类:
$factory->define(User::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // 密码 'remember_token' => Str::random(10), ]; });
这些是咱们建立的“假”用户的默认值。为此,咱们将生成一个 Seeder 文件:
php artisan make:seeder UsersSeeder
而后,咱们打开生成的文件database / seeds / UsersSeeder.php
,并用如下代码填充run()
方法:
public function run() { // This will create 100 users factory(App\User::class, 100)->create(); }
要运行它,咱们须要执行如下命令:
php artisan db:seed --class=UsersSeeder
您能够在Laravel官方文档中了解更多有关数据库种子的信息。
默认状况下,Laravel用户使用邮箱和密码进行身份验证。可是,若是您的用户标识不使用邮箱怎么办?例如,使用用户名做为标识。
您能够经过覆盖 traitAuthenticatesUsers.php
中的一种方法来轻松更改它。
这是默认值:
trait AuthenticatesUsers { // ... 其余方法 public function username() { return 'email'; }
您能够将其复制到您的LoginController.php
中,只需更改值便可:
class LoginController extends Controller { use AuthenticatesUsers; // ... 其余方法 public function username() { return 'username'; } }
让咱们更进一步。若是您想让用户可使用邮箱或用户名登陆怎么办?这样的话,用户能够在“邮箱/用户名”字段中选择其中一个填写。
让咱们向上面的username()
方法添加一个判断。咱们检查输入的字符串是不是电子邮件,若不是,则将其视为用户名。这是一个 PHP 函数,甚至不是 Laravel 函数。
class LoginController extends Controller { // ... public function username() { return filter_var(request('email'), FILTER_VALIDATE_EMAIL) ? 'email' : 'username'; } }
注意: 别忘了把登陆表单的input type="email"
改为type="text"
若是您尝试在同一分钟内使用无效凭据登陆五次以上,则请求会被拦截,并显示一条消息尝试登陆的次数过多。 请在X秒后重试。
该拦截操做将持续1分钟,而且对于用户的用户名/电子邮件及其IP地址是惟一的。
您能够自定义这些参数:
这两个参数在TraitThrottlesLogins
内部:
trait ThrottlesLogins { // ... other methods /** * Get the maximum number of attempts to allow. * * @return int */ public function maxAttempts() { return property_exists($this, 'maxAttempts') ? $this->maxAttempts : 5; } /** * Get the number of minutes to throttle for. * * @return int */ public function decayMinutes() { return property_exists($this, 'decayMinutes') ? $this->decayMinutes : 1; } }
所以,要覆盖这些属性,能够在 LoginController
内部指定属性:
class LoginController extends Controller { protected $maxAttempts = 3; // Default is 5 protected $decayMinutes = 2; // Default is 1 // ... }
默认状况下,新注册的用户将自动登陆并重定向到主页。
若是您须要禁用该功能并改成显示注册成功页面,而不自动登陆的话,能够执行如下操做。
原始注册方法位于 Trait RegistersUsers
的内部:
trait RegistersUsers { public function register(Request $request) { $this->validator($request->all())->validate(); event(new Registered($user = $this->create($request->all()))); $this->guard()->login($user); if ($response = $this->registered($request, $user)) { return $response; } return $request->wantsJson() ? new Response('', 201) : redirect($this->redirectPath()); }
所以,您的目标是在RegisterController
中覆盖它,而后重定向到新页面,而不是登陆:
class RegisterController extends Controller { use RegistersUsers; public function register(Request $request) { $this->validator($request->all())->validate(); event(new Registered($user = $this->create($request->all()))); return redirect()->route('your_success_page_route_name'); }
若是除了默认的电子邮件和密码外,还须要进行其余检查,该怎么办? 例如,您要检查用户是否处于活动状态或未被禁止。
您能够添加额外的字段 credentials
到定义在 AuthenticatesUsers
trait 的鉴权数组中:
trait AuthenticatesUsers { // ... protected function credentials(Request $request) { return $request->only($this->username(), 'password'); }
而后只须要重写 LoginController
便可:
class LoginController extends Controller { // ... protected function credentials(Request $request) { return $request->only($this->username(), 'password') + ['is_active' => 1]; }
注意: 这是一个颇有趣的便捷提示,可是我建议您在单独的中间件中执行这种额外的检查,而后向用户提供更明确的错误消息,而不是默认的凭证错误。
就是这些,都是一些便捷提示,可是自定义代码和外部扩展包还有不少能够发挥的地方。 所以,能够继续关注有关该主题的更多文章!
讨论请前往专业的 Laravel 论坛: https://learnku.com/laravel/t...