验证
- Introduction
- Authentication Quickstart
- Manually Authenticating Users
- HTTP Basic Authentication
- Logging Out
- Password Confirmation
- Adding Custom Guards
- Adding Custom User Providers
- Automatic Password Rehashing
- Social Authentication
- Events
介绍
许多 Web 应用程序都提供了用户身份验证和“登录”的方式。在 Web 应用程序中实现此功能可能非常复杂且存在风险。因此,Laravel 致力于为您提供快速、安全、轻松地实现身份验证所需的工具。
Laravel 的身份验证功能核心由“守卫”和“提供者”组成。守卫定义了每次请求时如何对用户进行身份验证。例如,Laravel 自带了一个session
使用会话存储和 Cookie 来维护状态的守卫。
提供程序定义如何从持久存储中检索用户。Laravel 已支持使用Eloquent检索用户和数据库查询构建器检索用户的支持。不过,你可以根据应用程序的需要自由定义其他提供程序。
应用程序的身份验证配置文件位于config/auth.php
。此文件包含几个详细记录的选项,用于调整 Laravel 身份验证服务的行为。
守卫和提供者不应与“角色”和“权限”混淆。要了解有关通过权限授权用户操作的更多信息,请参阅授权文档。
入门套件
想要快速上手?在全新的 Laravel 应用中安装Laravel 应用入门套件。迁移数据库后,在浏览器中访问/register
或任何其他分配给应用的 URL。入门套件将负责搭建您的整个身份验证系统!
即使您选择在最终的 Laravel 应用程序中不使用入门套件,安装入门套件也是一个绝佳的机会,让您学习如何在实际的 Laravel 项目中实现所有 Laravel 的身份验证功能。由于 Laravel 入门套件已包含身份验证控制器、路由和视图,您可以查看这些文件中的代码,以了解如何实现 Laravel 的身份验证功能。
数据库注意事项
默认情况下,Laravel在你的目录中包含一个App\Models\User
Eloquent 模型app/Models
。该模型可以与默认的 Eloquent 身份验证驱动程序一起使用。
如果您的应用程序未使用 Eloquent,则可以使用database
基于 Laravel 查询构建器的身份验证提供程序。如果您的应用程序使用的是 MongoDB,请查看 MongoDB 的官方Laravel 用户身份验证文档。
在构建模型的数据库架构时App\Models\User
,请确保密码列的长度至少为 60 个字符。当然,users
新 Laravel 应用程序中包含的表迁移已经创建了一个超过此长度的列。
此外,您还应该验证您的users
(或等效的)表是否包含一个长度remember_token
为 100 个字符的可空字符串列。此列将用于存储用户在登录应用程序时选择“记住我”选项的令牌。同样,users
新 Laravel 应用程序中包含的默认表迁移已包含此列。
生态系统概述
Laravel 提供了几个与身份验证相关的软件包。在继续之前,我们将回顾一下 Laravel 中通用的身份验证生态系统,并讨论每个软件包的预期用途。
首先,了解身份验证的工作原理。使用 Web 浏览器时,用户将通过登录表单提供用户名和密码。如果这些凭据正确,应用程序会将已通过身份验证的用户信息存储在用户的会话中。中。发送到浏览器的 Cookie 包含会话 ID,以便后续对应用程序的请求能够将用户与正确的会话关联。收到会话 Cookie 后,应用程序将根据会话 ID 检索会话数据,并确认身份验证信息已存储在会话中,并将用户视为“已通过身份验证”。
当远程服务需要身份验证才能访问 API 时,由于没有 Web 浏览器,通常不会使用 Cookie 进行身份验证。相反,远程服务会在每次请求时向 API 发送一个 API 令牌。应用程序可以根据有效 API 令牌表验证传入的令牌,并“认证”该请求是由与该 API 令牌关联的用户执行的。
Laravel 的内置浏览器身份验证服务
Laravel 内置了身份验证和会话服务,通常通过Auth
和Session
Facades 访问。这些功能为从 Web 浏览器发起的请求提供基于 Cookie 的身份验证。它们提供一些方法,允许您验证用户的凭据并对用户进行身份验证。此外,这些服务会自动在用户的会话中存储正确的身份验证数据,并生成用户的会话 Cookie。本文档包含如何使用这些服务的讨论。
应用启动套件
正如本文档所述,您可以手动与这些身份验证服务交互,以构建应用自身的身份验证层。不过,为了帮助您更快地上手,我们发布了免费的入门套件,其中包含了强大且现代化的整个身份验证层框架。
Laravel 的 API 身份验证服务
Laravel 提供了两个可选包来帮助您管理 API 令牌并验证使用 API 令牌发出的请求:Passport和Sanctum。请注意,这两个库与 Laravel 内置的基于 Cookie 的身份验证库并不互斥。这两个库主要侧重于 API 令牌身份验证,而内置的身份验证服务则侧重于基于 Cookie 的浏览器身份验证。许多应用程序会同时使用 Laravel 内置的基于 Cookie 的身份验证服务和 Laravel 的某个 API 身份验证包。
Passport
Passport 是一个 OAuth2 身份验证提供程序,提供多种 OAuth2“授权类型”,允许您颁发各种类型的令牌。总的来说,这是一个健壮且复杂的 API 身份验证包。然而,大多数应用程序并不需要 OAuth2 规范提供的复杂功能,这可能会让用户和开发者感到困惑。此外,开发者一直以来都对如何使用 Passport 等 OAuth2 身份验证提供程序对 SPA 应用或移动应用进行身份验证感到困惑。
Sanctum
为了应对 OAuth2 的复杂性和开发者的困惑,我们着手构建一个更简单、更精简的身份验证包,它既可以处理来自 Web 浏览器的第一方 Web 请求,也可以处理通过令牌发出的 API 请求。Laravel Sanctum的发布实现了这一目标,对于那些除了 API 之外还需提供第一方 Web UI 的应用程序,或者由独立于后端 Laravel 应用的单页应用 (SPA) 驱动的应用程序,或者提供移动客户端的应用程序,它应该被视为首选和推荐的身份验证包。
Laravel Sanctum 是一个Mix Web/API 身份验证包,可以管理应用程序的整个身份验证Processes。这是因为当基于 Sanctum 的应用程序收到请求时,Sanctum 会首先判断该请求是否包含指向已验证会话的会话 Cookie。Sanctum 通过调用我们之前讨论过的 Laravel 内置身份验证服务来实现这一点。如果请求未通过会话 Cookie 进行身份验证,Sanctum 将检查请求中是否存在 API 令牌。如果存在 API 令牌,Sanctum 将使用该令牌对请求进行身份验证。要了解有关此过程的更多信息,请参阅 Sanctum 的“工作原理”文档。
总结和选择你的堆栈
总之,如果您的应用程序将使用浏览器访问,并且您正在构建单片 Laravel 应用程序,则您的应用程序将使用 Laravel 的内置身份验证服务。
接下来,如果您的应用程序提供将被第三方使用的 API,您将在Passport或Sanctum之间进行选择,为您的应用程序提供 API 令牌身份验证。通常,如果可能的话,应该优先选择 Sanctum,因为它是一个简单、完整的 API 身份验证、SPA 身份验证和移动身份验证解决方案,包括对“作用域”或“能力”的支持。
如果您正在构建由 Laravel 后端支持的单页应用程序 (SPA),则应该使用Laravel Sanctum。使用 Sanctum 时,您需要手动实现自己的后端身份验证路由,或者使用Laravel Fortify作为无头身份验证后端服务,该服务为注册、密码重置、电子邮件验证等功能提供路由和控制器。
当您的应用程序绝对需要 OAuth2 规范提供的所有功能时,可以选择 Passport。
而且,如果您想快速入门,我们很高兴推荐我们的应用程序入门套件,作为启动新 Laravel 应用程序的快速方法,该应用程序已经使用我们首选的 Laravel 内置身份验证服务的身份验证堆栈。
身份验证快速入门
本文档的这一部分讨论了如何使用Laravel 应用程序入门套件进行用户身份验证,其中包含一些 UI 脚手架,可帮助您快速入门。如果您想直接集成 Laravel 的身份验证系统,请参阅手动验证用户身份的文档。
安装入门套件
首先,您需要安装 Laravel 应用程序入门套件。我们的入门套件提供了精美设计的起点,可帮助您将身份验证集成到全新的 Laravel 应用程序中。
检索已验证的用户
使用入门工具包创建应用程序并允许用户注册和验证身份后,您经常需要与当前已验证的用户进行交互。在处理传入请求时,您可以通过Auth
外观层的user
方法访问已验证的用户:
1use Illuminate\Support\Facades\Auth;2 3// Retrieve the currently authenticated user...4$user = Auth::user();5 6// Retrieve the currently authenticated user's ID...7$id = Auth::id();
或者,一旦用户通过身份验证,您可以通过Illuminate\Http\Request
实例访问已验证的用户。请记住,类型Prompts的类将自动注入到您的控制器方法中。通过对Illuminate\Http\Request
对象进行类型Prompts,您可以通过请求的方法从应用程序中的任何控制器方法方便地访问已验证的用户user
:
1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Http\RedirectResponse; 6use Illuminate\Http\Request; 7 8class FlightController extends Controller 9{10 /**11 * Update the flight information for an existing flight.12 */13 public function update(Request $request): RedirectResponse14 {15 $user = $request->user();16 17 // ...18 19 return redirect('/flights');20 }21}
确定当前用户是否已通过身份验证
要判断发出 HTTP 请求的用户是否已通过身份验证,可以使用Facadecheck
上的方法。如果用户已通过身份验证,Auth
此方法将返回:true
1use Illuminate\Support\Facades\Auth;2 3if (Auth::check()) {4 // The user is logged in...5}
尽管可以使用该方法确定用户是否已通过身份验证,但通常情况下,在允许用户访问某些路由/控制器之前,还是会使用中间件来验证用户是否已通过身份验证。要了解更多信息,请参阅保护路由check
的文档。
保护路线
路由中间件可用于仅允许经过身份验证的用户访问指定的路由。Laravel 内置了一个auth
中间件,它是该类的中间件别名Illuminate\Auth\Middleware\Authenticate
。由于 Laravel 内部已为该中间件设置了别名,因此您只需将其附加到路由定义即可:
1Route::get('/flights', function () {2 // Only authenticated users may access this route...3})->middleware('auth');
重定向未经身份验证的用户
当中间件检测到未经身份验证的用户时auth
,它会将用户重定向到login
指定的路由。你可以redirectGuestsTo
在应用程序bootstrap/app.php
文件中使用以下方法来修改此行为:
1use Illuminate\Http\Request;2 3->withMiddleware(function (Middleware $middleware) {4 $middleware->redirectGuestsTo('/login');5 6 // Using a closure...7 $middleware->redirectGuestsTo(fn (Request $request) => route('login'));8})
重定向已验证用户
当中间件检测到已通过身份验证的用户时guest
,它会将用户重定向到dashboard
或指定的路由。你可以在应用程序文件中home
使用以下方法修改此行为:redirectUsersTo
bootstrap/app.php
1use Illuminate\Http\Request;2 3->withMiddleware(function (Middleware $middleware) {4 $middleware->redirectUsersTo('/panel');5 6 // Using a closure...7 $middleware->redirectUsersTo(fn (Request $request) => route('panel'));8})
指定守卫
将中间件附加auth
到路由时,还可以指定使用哪个“守卫”来验证用户身份。指定的守卫应与配置文件guards
数组中的某个键相对应auth.php
:
1Route::get('/flights', function () {2 // Only authenticated users may access this route...3})->middleware('auth:admin');
登录限制
如果您使用我们的应用程序入门套件,系统将自动对登录尝试应用速率限制。默认情况下,如果用户多次尝试仍无法提供正确的凭据,则将在一分钟内无法登录。此限制仅适用于用户的用户名/电子邮件地址及其 IP 地址。
如果您想对应用程序中的其他路由进行速率限制,请查看速率限制文档。
手动验证用户
您无需使用 Laravel应用程序入门套件中附带的身份验证脚手架。如果您选择不使用此脚手架,则需要直接使用 Laravel 身份验证类来管理用户身份验证。别担心,这很容易!
Auth
我们将通过Facade访问 Laravel 的身份验证服务,因此需要确保Auth
在类顶部导入 Facade。接下来,我们来看一下该attempt
方法。该attempt
方法通常用于处理来自应用程序“登录”表单的身份验证尝试。如果身份验证成功,则应重新生成用户会话以防止会话固定:
1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Http\Request; 6use Illuminate\Http\RedirectResponse; 7use Illuminate\Support\Facades\Auth; 8 9class LoginController extends Controller10{11 /**12 * Handle an authentication attempt.13 */14 public function authenticate(Request $request): RedirectResponse15 {16 $credentials = $request->validate([17 'email' => ['required', 'email'],18 'password' => ['required'],19 ]);20 21 if (Auth::attempt($credentials)) {22 $request->session()->regenerate();23 24 return redirect()->intended('dashboard');25 }26 27 return back()->withErrors([28 'email' => 'The provided credentials do not match our records.',29 ])->onlyInput('email');30 }31}
该attempt
方法接受一个键/值对数组作为其第一个参数。数组中的值将用于在数据库表中查找用户。因此,在上面的示例中,将通过email
列的值检索用户。如果找到用户,则将存储在数据库中的哈希密码与password
通过数组传递给该方法的值进行比较。您不应该对传入请求的password
值进行哈希处理,因为框架会在将其与数据库中的哈希密码进行比较之前自动对其进行哈希处理。如果两个哈希密码匹配,则会为用户启动一个已认证会话。
请记住,Laravel 的身份验证服务将根据身份验证守卫的“提供程序”配置从数据库中检索用户。在默认config/auth.php
配置文件中,指定了 Eloquent 用户提供程序,并指示它App\Models\User
在检索用户时使用该模型。您可以根据应用程序的需求在配置文件中更改这些值。
如果身份验证成功,该attempt
方法将返回。否则,将返回。true
false
Laravel 重定向器提供的方法intended
会将用户重定向到他们试图访问的 URL,直到被身份验证中间件拦截。如果预期目标不可用,可以为此方法提供一个后备 URI。
指定附加条件
如果您愿意,除了用户的电子邮件和密码之外,您还可以在身份验证查询中添加额外的查询条件。为此,我们可以简单地将查询条件添加到传递给该attempt
方法的数组中。例如,我们可以验证用户是否被标记为“活跃”:
1if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {2 // Authentication was successful...3}
对于复杂的查询条件,你可以在凭证数组中提供一个闭包。此闭包将与查询实例一起调用,从而允许你根据应用程序的需求自定义查询:
1use Illuminate\Database\Eloquent\Builder;2 3if (Auth::attempt([4 'email' => $email,5 'password' => $password,6 fn (Builder $query) => $query->has('activeSubscription'),7])) {8 // Authentication was successful...9}
在这些示例中,email
不是必需选项,仅用作示例。您应该使用数据库表中与“用户名”对应的任何列名。
该attemptWhen
方法接收一个闭包作为第二个参数,可用于在实际验证用户身份之前对潜在用户进行更广泛的检查。闭包接收潜在用户,并返回true
或false
来指示用户是否可以进行身份验证:
1if (Auth::attemptWhen([2 'email' => $email,3 'password' => $password,4], function (User $user) {5 return $user->isNotBanned();6})) {7 // Authentication was successful...8}
访问特定的 Guard 实例
通过Auth
Facade 的guard
方法,您可以指定在验证用户身份时要使用的守卫实例。这允许您使用完全独立的可验证模型或用户表来管理应用程序各个部分的身份验证。
传递给方法的守卫名称guard
应该与配置文件中配置的守卫之一相对应auth.php
:
1if (Auth::guard('admin')->attempt($credentials)) {2 // ...3}
记住用户
许多 Web 应用程序在其登录表单上提供“记住我”复选框。如果您希望在应用程序中提供“记住我”功能,可以将布尔值作为第二个参数传递给该attempt
方法。
当此值为 时true
,Laravel 将无限期地保持用户身份验证,或直到用户手动注销。您的users
表必须包含字符串remember_token
列,该列将用于存储“记住我”令牌。users
新 Laravel 应用程序附带的表迁移已包含此列:
1use Illuminate\Support\Facades\Auth;2 3if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {4 // The user is being remembered...5}
如果您的应用程序提供“记住我”功能,您可以使用该viaRemember
方法来确定当前经过身份验证的用户是否使用“记住我” cookie 进行了身份验证:
1use Illuminate\Support\Facades\Auth;2 3if (Auth::viaRemember()) {4 // ...5}
其他身份验证方法
验证用户实例
如果需要将现有用户实例设置为当前已验证的用户,可以将该用户实例传递给Auth
Facade 的方法。给定的用户实例必须是contractlogin
的实现。Laravel内置的模型已经实现了此接口。这种身份验证方法在你已经拥有有效的用户实例时非常有用,例如用户在应用程序中注册后立即生效:Illuminate\Contracts\Auth\Authenticatable
App\Models\User
1use Illuminate\Support\Facades\Auth;2 3Auth::login($user);
您可以将布尔值作为第二个参数传递给该login
方法。此值指示已验证会话是否需要“记住我”功能。请记住,这意味着会话将无限期地保持验证状态,或者直到用户手动退出应用程序为止:
1Auth::login($user, $remember = true);
如果需要,您可以在调用该方法之前指定身份验证保护login
:
1Auth::guard('admin')->login($user);
通过 ID 验证用户身份
要使用数据库记录的主键对用户进行身份验证,可以使用该loginUsingId
方法。此方法接受要身份验证的用户的主键:
1Auth::loginUsingId(1);
remember
您可以向该方法的参数传递一个布尔值loginUsingId
。此值指示已验证会话是否需要“记住我”功能。请记住,这意味着会话将无限期地进行身份验证,或者直到用户手动退出应用程序为止:
1Auth::loginUsingId(1, remember: true);
验证用户一次
您可以使用该once
方法在单个请求中向应用程序验证用户身份。调用此方法时不会使用任何会话或 Cookie:
1if (Auth::once($credentials)) {2 // ...3}
HTTP 基本身份验证
HTTP 基本身份验证提供了一种快速验证应用程序用户身份的方法,无需设置专用的“登录”页面。首先,将auth.basic
中间件附加到路由。该auth.basic
中间件已包含在 Laravel 框架中,因此您无需自行定义:
1Route::get('/profile', function () {2 // Only authenticated users may access this route...3})->middleware('auth.basic');
将中间件附加到路由后,在浏览器中访问该路由时,系统会自动Prompts您输入凭据。默认情况下,auth.basic
中间件会假定数据库表email
中的列users
是用户的“用户名”。
关于 FastCGI 的说明
如果您使用 PHP FastCGI 和 Apache 来服务您的 Laravel 应用程序,HTTP Basic 身份验证可能无法正常工作。为了解决这些问题,可以在应用程序.htaccess
文件中添加以下几行:
1RewriteCond %{HTTP:Authorization} ^(.+)$2RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
无状态 HTTP 基本身份验证
您也可以使用 HTTP 基本身份验证,而无需在会话中设置用户标识符 Cookie。如果您选择使用 HTTP 身份验证来验证应用程序 API 的请求,这将非常有用。为此,请定义一个调用该onceBasic
方法的中间件。如果该方法未返回任何响应onceBasic
,则请求可能会被传递到应用程序中:
1<?php 2 3namespace App\Http\Middleware; 4 5use Closure; 6use Illuminate\Http\Request; 7use Illuminate\Support\Facades\Auth; 8use Symfony\Component\HttpFoundation\Response; 9 10class AuthenticateOnceWithBasicAuth11{12 /**13 * Handle an incoming request.14 *15 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next16 */17 public function handle(Request $request, Closure $next): Response18 {19 return Auth::onceBasic() ?: $next($request);20 }21 22}
接下来,将中间件附加到路由:
1Route::get('/api/user', function () {2 // Only authenticated users may access this route...3})->middleware(AuthenticateOnceWithBasicAuth::class);
注销
要手动将用户从应用程序中注销,您可以使用外观层logout
提供的方法Auth
。这将从用户的会话中删除身份验证信息,以便后续请求不再进行身份验证。
除了调用该方法之外logout
,建议您使用户会话无效并重新生成其CSRF 令牌。注销用户后,通常应将用户重定向到应用程序的根目录:
1use Illuminate\Http\Request; 2use Illuminate\Http\RedirectResponse; 3use Illuminate\Support\Facades\Auth; 4 5/** 6 * Log the user out of the application. 7 */ 8public function logout(Request $request): RedirectResponse 9{10 Auth::logout();11 12 $request->session()->invalidate();13 14 $request->session()->regenerateToken();15 16 return redirect('/');17}
使其他设备上的会话无效
Laravel 还提供了一种机制,可以使用户在其他设备上处于Events状态的会话失效并“注销”,而不会使当前设备上的会话失效。此功能通常用于当用户更改或更新密码时,您希望使其他设备上的会话失效,同时保持当前设备处于身份验证状态。
在开始之前,你应该确保Illuminate\Session\Middleware\AuthenticateSession
中间件已包含在需要接收会话身份验证的路由中。通常,你应该将此中间件放在路由组定义中,以便它可以应用于应用程序的大多数路由。默认情况下,可以使用中间件别名AuthenticateSession
将中间件附加到路由:auth.session
1Route::middleware(['auth', 'auth.session'])->group(function () {2 Route::get('/', function () {3 // ...4 });5});
然后,您可以使用外观logoutOtherDevices
提供的方法Auth
。此方法要求用户确认其当前密码,您的应用程序应通过输入表单接受该密码:
1use Illuminate\Support\Facades\Auth;2 3Auth::logoutOtherDevices($currentPassword);
当调用该logoutOtherDevices
方法时,用户的其他会话将完全失效,这意味着他们将被“注销”他们之前经过身份验证的所有警卫。
密码确认
在构建应用程序时,您偶尔可能会遇到需要用户在操作执行之前或重定向到应用程序敏感区域之前确认密码的情况。Laravel 内置了中间件,可以简化此过程。实现此功能需要定义两个路由:一个路由显示一个视图,要求用户确认密码;另一个路由确认密码有效并将用户重定向到预期目标。
以下文档讨论了如何直接与 Laravel 的密码确认功能集成;但是,如果您想更快地开始,Laravel 应用程序入门套件包含对此功能的支持!
配置
确认密码后,用户三小时内无需再次确认密码。不过,您可以通过更改password_timeout
应用程序config/auth.php
配置文件中配置值的值来配置再次Prompts用户输入密码之前的时间长度。
路由
密码确认表
首先,我们将定义一个路由来显示请求用户确认密码的视图:
1Route::get('/confirm-password', function () {2 return view('auth.confirm-password');3})->middleware('auth')->name('password.confirm');
正如您所料,此路由返回的视图应该有一个包含password
字段的表单。此外,您可以在视图中添加一些文本,以说明用户正在进入应用程序的受保护区域,并且必须确认其密码。
确认密码
接下来,我们将定义一个路由,用于处理来自“确认密码”视图的表单请求。该路由负责验证密码并将用户重定向到其预期的目的地:
1use Illuminate\Http\Request; 2use Illuminate\Support\Facades\Hash; 3use Illuminate\Support\Facades\Redirect; 4 5Route::post('/confirm-password', function (Request $request) { 6 if (! Hash::check($request->password, $request->user()->password)) { 7 return back()->withErrors([ 8 'password' => ['The provided password does not match our records.'] 9 ]);10 }11 12 $request->session()->passwordConfirmed();13 14 return redirect()->intended();15})->middleware(['auth', 'throttle:6,1']);
在继续之前,让我们更详细地研究一下这个路由。首先,我们password
需要确定请求的字段是否与已验证用户的密码匹配。如果密码有效,我们需要通知 Laravel 的会话,用户已确认密码。该passwordConfirmed
方法会在用户会话中设置一个时间戳,Laravel 可以使用该时间戳来确定用户上次确认密码的时间。最后,我们可以将用户重定向到他们想要的目的地。
保护路线
您应该确保任何执行需要最近密码确认的操作的路由都已分配了password.confirm
中间件。此中间件包含在 Laravel 的默认安装中,它会自动将用户的预期目标存储在会话中,以便用户在确认密码后可以重定向到该位置。在将用户的预期目标存储在会话中后,中间件会将用户重定向到password.confirm
指定的路由:
1Route::get('/settings', function () {2 // ...3})->middleware(['password.confirm']);4 5Route::post('/settings', function () {6 // ...7})->middleware(['password.confirm']);
添加自定义防护
extend
您可以使用 Facade上的方法定义自己的身份验证保护Auth
。您应该将对该方法的调用放在服务提供者extend
中。由于 Laravel 已经自带了,我们可以将代码放在该提供者中:AppServiceProvider
1<?php 2 3namespace App\Providers; 4 5use App\Services\Auth\JwtGuard; 6use Illuminate\Contracts\Foundation\Application; 7use Illuminate\Support\Facades\Auth; 8use Illuminate\Support\ServiceProvider; 9 10class AppServiceProvider extends ServiceProvider11{12 // ...13 14 /**15 * Bootstrap any application services.16 */17 public function boot(): void18 {19 Auth::extend('jwt', function (Application $app, string $name, array $config) {20 // Return an instance of Illuminate\Contracts\Auth\Guard...21 22 return new JwtGuard(Auth::createUserProvider($config['provider']));23 });24 }25}
如上例所示,传递给该extend
方法的回调应该返回 的实现Illuminate\Contracts\Auth\Guard
。该接口包含一些定义自定义守卫所需的方法。自定义守卫定义完成后,您可以在配置文件guards
的配置中引用该守卫auth.php
:
1'guards' => [2 'api' => [3 'driver' => 'jwt',4 'provider' => 'users',5 ],6],
关闭请求守卫
实现自定义的基于 HTTP 请求的身份验证系统的最简单方法是使用该Auth::viaRequest
方法。此方法允许您使用单个闭包快速定义身份验证过程。
首先,在应用程序的 方法Auth::viaRequest
中调用该方法。该方法接受身份验证驱动程序名称作为其第一个参数。此名称可以是任何描述自定义防护的字符串。传递给该方法的第二个参数应该是一个闭包,它接收传入的 HTTP 请求并返回用户实例,或者,如果身份验证失败,则:boot
AppServiceProvider
viaRequest
null
1use App\Models\User; 2use Illuminate\Http\Request; 3use Illuminate\Support\Facades\Auth; 4 5/** 6 * Bootstrap any application services. 7 */ 8public function boot(): void 9{10 Auth::viaRequest('custom-token', function (Request $request) {11 return User::where('token', (string) $request->token)->first();12 });13}
一旦定义了自定义身份验证驱动程序,您就可以在配置文件guards
的配置中将其配置为驱动程序auth.php
:
1'guards' => [2 'api' => [3 'driver' => 'custom-token',4 ],5],
最后,你可以在将身份验证中间件分配给路由时引用守卫:
1Route::middleware('auth:api')->group(function () {2 // ...3});
添加自定义用户提供程序
如果您不使用传统的关系型数据库来存储用户,则需要使用自己的身份验证用户提供程序来扩展 Laravel。我们将使用Facadeprovider
上的方法Auth
来定义自定义用户提供程序。用户提供程序解析器应返回以下实现Illuminate\Contracts\Auth\UserProvider
:
1<?php 2 3namespace App\Providers; 4 5use App\Extensions\MongoUserProvider; 6use Illuminate\Contracts\Foundation\Application; 7use Illuminate\Support\Facades\Auth; 8use Illuminate\Support\ServiceProvider; 9 10class AppServiceProvider extends ServiceProvider11{12 // ...13 14 /**15 * Bootstrap any application services.16 */17 public function boot(): void18 {19 Auth::provider('mongo', function (Application $app, array $config) {20 // Return an instance of Illuminate\Contracts\Auth\UserProvider...21 22 return new MongoUserProvider($app->make('mongo.connection'));23 });24 }25}
使用该方法注册提供程序后provider
,您可以在配置文件中切换到新的用户提供程序auth.php
。首先,定义一个provider
使用新驱动程序的配置文件:
1'providers' => [2 'users' => [3 'driver' => 'mongo',4 ],5],
最后,您可以在guards
配置中引用此提供程序:
1'guards' => [2 'web' => [3 'driver' => 'session',4 'provider' => 'users',5 ],6],
用户提供者Contracts
Illuminate\Contracts\Auth\UserProvider
实现负责Illuminate\Contracts\Auth\Authenticatable
从持久存储系统(例如 MySQL、MongoDB 等)中获取实现。这两个接口允许 Laravel 身份验证机制继续运行,无论用户数据如何存储或使用什么类型的类来表示经过身份验证的用户:
我们来看一下Illuminate\Contracts\Auth\UserProvider
Contracts:
1<?php 2 3namespace Illuminate\Contracts\Auth; 4 5interface UserProvider 6{ 7 public function retrieveById($identifier); 8 public function retrieveByToken($identifier, $token); 9 public function updateRememberToken(Authenticatable $user, $token);10 public function retrieveByCredentials(array $credentials);11 public function validateCredentials(Authenticatable $user, array $credentials);12 public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false);13}
该retrieveById
函数通常接收一个代表用户的键,例如来自 MySQL 数据库的自增 ID。Authenticatable
该方法应检索并返回与该 ID 匹配的实现。
该函数通过用户的唯一标识和“记住我”retrieveByToken
来检索用户,这些标识通常存储在数据库中的列中,例如。与上一个方法一样,此方法应返回具有匹配令牌值的实现。$identifier
$token
remember_token
Authenticatable
该方法使用新的updateRememberToken
更新$user
实例。当“记住我”身份验证尝试成功或用户注销时,会为用户分配一个新的令牌。remember_token
$token
该retrieveByCredentials
方法接收在尝试向应用程序进行身份验证时传递给它的凭据数组Auth::attempt
。然后,该方法应该“查询”底层持久存储,以查找与这些凭据匹配的用户。通常,此方法将运行带有“where”条件的查询,以搜索“用户名”与 的值匹配的用户记录$credentials['username']
。该方法应该返回 的实现Authenticatable
。此方法不应尝试执行任何密码验证或身份验证。
该validateCredentials
方法应将给定的$user
与进行比较$credentials
,以验证用户身份。例如,此方法通常使用Hash::check
方法来比较 的值$user->getAuthPassword()
与 的值$credentials['password']
。此方法应返回true
或 ,false
以指示密码是否有效。
如果需要且支持,该rehashPasswordIfRequired
方法应该重新哈希给定$user
的 密码。例如,此方法通常会使用Hash::needsRehash
方法来判断该$credentials['password']
值是否需要重新哈希。如果需要重新哈希密码,则该方法应该使用Hash::make
方法来重新哈希密码,并更新底层持久存储中的用户记录。
可验证合约
现在我们已经探索了 上的每个方法UserProvider
,让我们看一下Authenticatable
契约。记住,用户提供者应该从retrieveById
、retrieveByToken
和retrieveByCredentials
方法返回此接口的实现:
1<?php 2 3namespace Illuminate\Contracts\Auth; 4 5interface Authenticatable 6{ 7 public function getAuthIdentifierName(); 8 public function getAuthIdentifier(); 9 public function getAuthPasswordName();10 public function getAuthPassword();11 public function getRememberToken();12 public function setRememberToken($value);13 public function getRememberTokenName();14}
这个接口很简单。getAuthIdentifierName
方法应该返回用户的“主键”列的名称,getAuthIdentifier
方法应该返回用户的“主键”。当使用 MySQL 后端时,这很可能是分配给用户记录的自增主键。方法getAuthPasswordName
应该返回用户密码列的名称。getAuthPassword
方法应该返回用户的哈希密码。
此接口允许身份验证系统与任何“用户”类协同工作,无论您使用哪种 ORM 或存储抽象层。默认情况下,LaravelApp\Models\User
在app/Models
目录中包含一个实现此接口的类。
自动密码重新哈希
config/hashing.php
Laravel 默认的密码哈希算法是 bcrypt。您可以通过应用程序的配置文件或环境变量来调整 bcrypt 哈希的“工作因子” BCRYPT_ROUNDS
。
通常,随着 CPU / GPU 处理能力的提升,bcrypt 的工作因子应该逐渐增加。如果你为应用程序增加了 bcrypt 的工作因子,当用户通过 Laravel 的入门套件在你的应用程序上进行身份验证,或者当你通过该方法手动验证用户attempt
身份时,Laravel 将会优雅地自动重新哈希用户密码。
通常,自动密码重新哈希不会破坏您的应用程序;但是,您可以通过发布hashing
配置文件来禁用此行为:
1php artisan config:publish hashing
一旦配置文件发布,您可以将rehash_on_login
配置值设置为false
:
1'rehash_on_login' => false,
Events
Laravel在身份验证过程中会调度各种事件。您可以为以下任何事件定义监听器:
事件名称 |
---|
Illuminate\Auth\Events\Registered |
Illuminate\Auth\Events\Attempting |
Illuminate\Auth\Events\Authenticated |
Illuminate\Auth\Events\Login |
Illuminate\Auth\Events\Failed |
Illuminate\Auth\Events\Validated |
Illuminate\Auth\Events\Verified |
Illuminate\Auth\Events\Logout |
Illuminate\Auth\Events\CurrentDeviceLogout |
Illuminate\Auth\Events\OtherDeviceLogout |
Illuminate\Auth\Events\Lockout |
Illuminate\Auth\Events\PasswordReset |