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