用户认证
认证
介绍
许多网络应用程序为用户提供了一种通过应用程序进行身份验证和“登录”的方式。在网络应用程序中实现此功能可能是一个复杂且潜在风险的任务。因此,Laravel致力于为您提供所需的工具,以快速、安全和轻松地实现认证。
在核心层面,Laravel的认证设施由“守卫”和“提供者”组成。守卫定义了如何为每个请求对用户进行身份验证。例如,Laravel提供了一个session
守卫,它使用会话存储和cookie来维护状态。
提供者定义了如何从持久性存储中检索用户。Laravel支持使用 Eloquent 和数据库查询构建器来检索用户。但是,您可以根据需要自由定义其他提供者以适应您的应用程序。
在Laravel中,你可以通过config/auth.php
文件来配置应用程序的认证设置。这个文件包含了许多经过详细记录的选项,可以调整Laravel的认证服务的行为。
注意:
守卫(guards)和提供者(providers)不应与「角色」和「权限」混淆。如果想了解如何通过权限授权用户操作,请参考授权文档。
入门套件
想要快速开始吗?在一个新的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
(或等效)表包含一个可为空的、长度为 100 个字符的字符串 remember_token
列。这个列将用于存储选择了「记住我」选项的用户的令牌,当他们登录到您的应用程序时。同样,新的 Laravel 应用程序中包含的默认users
表迁移已经包含了这个列。
生态系统概述
Laravel提供了几个与认证相关的包。在继续之前,让我们回顾一下 Laravel 中的通用认证生态系统,并讨论每个包的预期目的。
首先,考虑认证的工作原理。当用户使用 Web 浏览器时,他们将通过登录表单提供他们的用户名和密码。如果这些凭据正确,应用程序将在用户的会话中存储有关已认证用户的信息。向浏览器发出的 cookie 包含会话 ID,以便应用程序的后续请求可以将用户与正确的会话关联起来。收到会话 cookie 后,应用程序将根据会话ID检索会话数据,注意已将认证信息存储在会话中,并将用户视为「已认证」。
当远程服务需要进行身份验证以访问API时,通常不会使用 cookie 进行身份验证,因为没有Web 浏览器。相反,远程服务在每个请求中向 API 发送 API 令牌。应用程序可以根据有效 API令牌表验证传入的令牌,并将请求「认证」为与该 API 令牌关联的用户执行的请求。
Laravel 的内置浏览器认证服务包括通过 Auth
和 Session
门面访问的内置认证和会话服务。这些功能为从 Web 浏览器发起的请求提供基于 cookie 的身份验证。它们提供了允许你验证用户凭据并对用户进行身份验证的方法。此外,这些服务将自动将正确的身份验证数据存储在用户的会话中,并发出用户的会话 cookie。如何使用这些服务的讨论包含在本文档中。
应用程序入门套件
正如本文档中所讨论的,你可以手动与这些认证服务交互,构建应用程序自己的认证层。然而,为了帮助您更快地开始,我们发布了提供完整、现代化脚手架的免费包,涵盖整个认证层。这些包括Laravel Breeze、Laravel Jetstream和Laravel Fortify。
-
Laravel Breeze是Laravel所有认证功能的简单、最小实现,包括登录、注册、密码重置、电子邮件验证和密码确认。Laravel Breeze 的视图层由简单的Blade模板组成,使用Tailwind CSS进行样式设计。要开始使用,请查看Laravel的应用程序入门套件文档。
-
Laravel Fortify 是 Laravel 的无头身份验证后端,实现了本文档中许多功能,包括基于cookie 的身份验证,以及其他功能,如双因素身份验证和电子邮件验证。Fortify为Laravel Jetstream提供身份验证后端,也可以与Laravel Sanctum结合独立使用,为需要与Laravel进行身份验证的SPA提供身份验证。
Laravel Jetstream是一个强大的应用程序入门套件,它消费并暴露了Laravel Fortify的身份验证服务,具有由Tailwind CSS、Livewire和/或Inertia提供支持的美观、现代化的用户界面。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时得以实现,应该被视为首选和推荐的身份验证包,适用于将提供第一方Web用户界面以及API,或由单页面应用程序(SPA)提供支持的应用程序,该SPA与后端Laravel应用程序分开存在,或提供移动客户端的应用程序。
Laravel Sanctum 是一个混合了 web 和 API 的身份验证包,它让我们管理应用程序的整个身份验证过程成为可能,因为当基于 Sanctum 的应用程序收到请求时,Sanctum 将首先确定请求是否包含引用已验证 session 的 session cookie。Sanctum 通过调用我们前面讨论过的 Laravel 的内置身份验证服务来实现这一点。如果请求没有通过 session cookie 进行身份验证,Sanctum 将检查请求中的 API 令牌。如果存在 API 令牌,则 Sanctum 将使用该令牌对请求进行身份验证。要了解有关此过程的更多信息,请参阅 Sanctum 的 工作原理 文档。
Laravel Sanctum 是我们选择与 Laravel Jetstream 应用程序入门套件一起使用的 API 包,因为我们认为它最适合大多数 web 应用程序的身份验证需求。
汇总 & 选择你的解决方案
总之,如果你的应用程序将使用浏览器访问,并且你正在构建一个单页面的 Laravel 应用程序,那么你的应用程序可以使用 Laravel 的内置身份验证服务
接下来,如果你的应用程序提供将由第三方使用的 API ,你可以在 Passport 或 Sanctum 之间进行选择,为你的应用程序提供 API 令牌身份验证。一般来说,尽可能选择 Sanctum,因为它是 API 认证、SPA 认证和移动认证的简单、完整的解决方案,包括对「scopes」或「abilities」的支持。
如果你正在构建一个将由 Laravel 后端支持的单页面应用程序(SPA),那么应该使用 Laravel Sanctum。在使用 Sanctum 时,你需要 手动实现自己的后端验证路由 或使用 Laravel Fortify 作为无 header 身份验证后端服务,为注册、密码重置、电子邮件验证等功能提供路由和控制器。
当应用程序确定必须使用 OAuth2 规范提供的所有特性时,可以选择 Passport。
而且,如果你想快速入门,我们很高兴推荐 Laravel Breeze 作为启动新 Laravel 应用程序的快速方法,该应用程序已经使用了我们首选的 Laravel 内置身份验证服务和 Laravel Sanctum 身份验证技术栈。
身份验证快速入门
警告
文档的这一部分讨论了通过 Laravel 应用入门套件 对用户进行身份验证,其中包括可帮助你快速入门的 UI 脚手架。如果你想直接与 Laravel 的身份验证系统集成,请查看 手动验证用户 上的文档。
安装入门套件
首先,你应该 安装 Laravel 应用入门套件。我们当前的入门套件 Laravel Breeze 和 Laravel Jetstream 提供了设计精美的起点,可将身份验证纳入你的全新 Laravel 应用程序。
Laravel Breeze 是 Laravel 所有身份验证功能的最小型简单的实现,包括登录、注册、密码重置、电子邮件验证和密码确认。 Laravel Breeze 的视图层由简单的 Blade templates 和 Tailwind CSS 组成。Breeze 还使用 Vue 或 React 提供了基于 Inertia 的脚手架选项。
Laravel Jetstream 是一个更强大的应用入门套件,它支持使用 Livewire 或 Inertia and Vue 来构建你的应用程序。 此外,Jetstream 还提供可选的双因素身份验证支持、团队、资料管理、浏览器 session 管理、通过 Laravel Sanctum 的 API 支持、帐户删除等。
获取已认证的用户信息
在安装身份验证入门套件并允许用户注册应用程序并对其进行身份验证之后,你通常需要与当前通过身份验证的用户进行交互。在处理传入请求时,你可以通过 Auth
facade 的 user
方法访问通过身份验证的用户:
use Illuminate\Support\Facades\Auth;
// 获取当前的认证用户信息...
$user = Auth::user();
// 获取当前的认证用户ID...
$id = Auth::id();
或者,一旦用户通过身份验证,你就可以通过 Illuminate\Http\Request
实例访问通过身份验证的用户。请记住,使用类型提示的类将自动注入到控制器方法中。通过对 Illuminate\Http\Request
对象进行类型提示,你可以通过 Request 的 user
方法从应用程序中的任何控制器方法方便地访问通过身份验证的用户:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class FlightController extends Controller
{
/**
* 更新现有航班的航班信息。
*/
public function update(Request $request): RedirectResponse
{
$user = $request->user();
// ...
return redirect('/flights');
}
}
确定当前用户是否已通过身份验证
要确定发出传入 HTTP 请求的用户是否通过身份验证,你可以在 Auth
facade 上使用 check
方法。如果用户通过身份验证,此方法将返回 true
:
use Illuminate\Support\Facades\Auth;
if (Auth::check()) {
// 该用户已登录...
}
注意
尽管可以使用check
方法确定用户是否已通过身份验证,但在允许用户访问某些路由 / 控制器之前,你通常会使用中间件验证用户是否已通过身份验证。要了解更多信息,请查看有关 路由保护 的文档。
保护路由
路由中间件 可用于仅允许通过身份验证的用户访问给定路由。 Laravel 附带了一个 auth
中间件, 它是 Illuminate\Auth\Middleware\Authenticate
类的 中间件别名 。 这个中间件已经在 Laravel 内部别名化,所以您需要做的只是将中间件附加到路由定义上:
Route::get('/flights', function () {
// 只有经过身份验证的用户才能访问此路由...
})->middleware('auth');
给未认证的用户设置重定向
当 auth
中间件检测到未经身份验证的用户时,它将用户重定向到 login
命名路由。 你可以通过更新应用程序的 bootstrap/app.php
文件中的 redirectGuestsTo
方法来修改此行为:
use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware) {
$middleware->redirectGuestsTo('/login');
// 使用闭包...
$middleware->redirectGuestsTo(fn (Request $request) => route('login'));
})
指定看守器
将 auth
中间件附加到路由时,你还可以指定应该使用哪个「guard」来验证用户。指定的 guard 应与 auth.php
配置文件的 guards
数组中的一个键相对应:
Route::get('/flights', function () {
// 只有经过身份验证的用户才能访问此路由...
})->middleware('auth:admin');
登录限流
如果你使用的是 Laravel Breeze 或 Laravel Jetstream 入门套件,那么在尝试登录的时候将自动应用速率限制。默认情况下,如果用户在多次尝试后未能提供正确的凭据,他们将在一分钟内无法登录。该限制对与用户的用户名 / 电子邮件地址及其 IP 地址是唯一的。
注意
如果你想对应用程序中的其他路由进行速率限制,请查看 速率限制 文档。
手动验证用户
你并非一定要使用 Laravel 的 应用入门套件 附带的身份验证脚手架。如果你选择不使用这个脚手架,则需要直接使用 Laravel 身份验证类来管理用户身份验证。别担心,这也很容易!
我们将通过 Auth
facade来访问 Laravel 的身份认证服务,因此我们需要确保在类的顶部导入 Auth
facade。接下来,让我们看下 attempt
方法。attempt
方法通常用于处理应用程序「登录」表单的身份认证尝试。如果认证成功,你应当重新生成用户的session 以防止 session 固化:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* 处理身份认证尝试。
*/
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
值进行 hash 处理,因为框架会在将值与数据库中的 hash 密码进行比较之前自动对其进行 hash 处理。如果两个 hash 密码匹配,则将为该用户启动经过认证的会话。
请记住,Laravel 的身份认证服务将基于你的身份认证守卫的「provider」配置从数据库检索用户。在默认的 config/auth.php
配置文件中,指定了 Eloquent 用户提供者,并指示其在检索用户时使用 App\Models\User
模型。根据应用程序的需求,你可以在配置文件中更改这些值。
如果身份验证成功,attempt
方法将返回 true
。否则,将返回 false
。
Laravel 的重定向器提供的 intended
方法会将用户重定向到他们在被身份验证中间件拦截之前尝试访问的 URL。可以给此方法提供一个回退 URI,以防预期的目的地不可用。
指定附加条件
如果你愿意,除了用户的电子邮件和密码外,还可以向身份认证查询中添加额外的查询条件。为此,我们可以简单地将查询条件添加到传递给 attempt
方法的数组中。例如,我们可以验证用户是否被标记为「active」:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// 认证成功...
}
对于复杂的查询条件,你可以在凭证数组中提供闭包。此闭包将与查询实例一起调用,允许你根据应用程序的需要自定义查询:
use Illuminate\Database\Eloquent\Builder;
if (Auth::attempt([
'email' => $email,
'password' => $password,
fn (Builder $query) => $query->has('activeSubscription'),
])) {
// 认证成功...
}
警告
在这些例子中,
attemptWhen
方法接收一个闭包作为其第二个参数,可用于在实际验证用户之前对潜在用户执行更广泛的检查。闭包接收潜在用户并应返回 true
或 false
以指示用户是否可以通过身份验证:
if (Auth::attemptWhen([
'email' => $email,
'password' => $password,
], function (User $user) {
return $user->isNotBanned();
})) {
// 认证成功...
}
访问特定的守卫实例
通过 Auth
facade 的 guard
方法,你可以指定在对用户进行身份验证时要使用哪个 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)) {
// 用户将被记住...
}
如果你的应用程序提供「记住我」的功能,你可以使用 viaRemember
方法来确定当前通过身份验证的用户是否使用「记住我」cookie 进行了身份验证:
use Illuminate\Support\Facades\Auth;
if (Auth::viaRemember()) {
// ...
}
其他身份验证方法
验证用户实例
如果你需要将已有的用户实例设置为当前认证的用户,你可以将用户实例传递给 Auth
facade 的 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
方法。此值指示通过身份验证的 session 是否需要「记住我」功能。请记住,这意味着 session 将无限期地进行身份验证,或者直到用户手动注销应用程序为止:
Auth::loginUsingId(1, $remember = true);
只验证一次
你可以使用 once
方法通过应用程序对单个请求的用户进行身份验证。调用此方法时不会使用 session 或 cookie:
if (Auth::once($credentials)) {
// ...
}
HTTP Basic 用户认证
HTTP Basic 用户认证 提供了一种快速的方式来认证你的应用用户,而不需要设置专门的「登录」页面。要开始,只需要将 auth.basic
中间件附加到一个路由上。auth.basic
中间件已包含在 Laravel 框架内,因此你不需要定义它:
Route::get('/profile', function () {
// 只有认证用户才能访问此路由...
})->middleware('auth.basic');
一旦中间件被附加到路由上,当你在浏览器中访问该路由时,系统会自动提示你输入凭证。默认情况下,auth.basic
中间件会假设你的 users
数据库表中的 email
列是用户的「用户名」。
关于 FastCGI 的说明
如果你使用 PHP FastCGI 和 Apache 来服务你的 Laravel 应用程序,HTTP Basic 认证可能无法正常工作。为了纠正这些问题,可以在应用程序的 .htaccess
文件中添加以下行:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
无状态 HTTP Basic 认证
你也可以使用 HTTP Basic 认证,而不在会话中设置用户标识符 cookie。如果你选择使用 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
{
/**
* 处理传入的请求。
*
* @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 () {
// 只有认证用户才能访问此路由...
})->middleware(AuthenticateOnceWithBasicAuth::class);
登出
为了手动将用户从你的应用程序中登出,你可以使用 Auth
facade 提供的 logout
方法。这将从用户的会话中移除认证信息,这样后续请求就不会被认证。
除了调用 logout
方法外,建议你将用户的 session 置为过期,并重新生成其 CSRF 令牌。注销用户后,通常会将用户重定向到应用程序的根目录:
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
/**
* 将用户从应用程序中登出。
*/
public function logout(Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
使其他设备上的 session 失效
Laravel 还提供了这样一种机制,可以使在其他设备上处于活动状态的用户 session 无效和「登出」,而不会使其当前设备上的 session 失效。当用户正在更改或更新其密码,并且你希望在保持当前设备身份验证的同时使其他设备上的 session 无效时,通常会使用此功能。
在开始之前,你应该确保 Illuminate\Session\Middleware\AuthenticateSession
中间件包含在应该接收会话认证的路由上。通常,你应该将这个中间件放到路由组定义中,这样它就可以应用于应用程序的大多数路由。默认情况下,AuthenticateSession
中间件可以使用 auth.session
中间件别名附加到路由:
Route::middleware(['auth', 'auth.session'])->group(function () {
Route::get('/', function () {
// ...
});
});
然后,你可以使用 Auth
facade 提供的 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 的 session 用户已经确认了他们的密码。passwordConfirmed
方法将在用户的 session 中设置一个时间戳,Laravel 可以使用它来确定用户上次确认密码的时间。最后,我们可以将用户重定向到他们想要的目的地。
保护路由
你应该确保为执行需要最近确认密码的操作的路由被分配到 password.confirm
中间件。此中间件包含在 Laravel 的默认安装中,并且会自动将用户的预期目的地存储在 session 中,以便用户在确认密码后可以重定向到该位置。在 session 中存储用户的预期目的地之后,中间件将用户重定向到 password.confirm
的 命名路由:
Route::get('/settings', function () {
// ...
})->middleware(['password.confirm']);
Route::post('/settings', function () {
// ...
})->middleware(['password.confirm']);
添加自定义守卫
你可以使用 Auth
facade 上的 extend
方法定义你自己的身份验证守卫。你应该在 服务提供者中调用 extend
方法。 由于 Laravel 已经附带了 AuthServiceProvider
,因此我们可以将代码放置在该提供者中:
<?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
{
// ...
/**
* 启动任何应用服务
*/
public function boot(): void
{
Auth::extend('jwt', function (Application $app, string $name, array $config) {
// 返回 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
方法。此方法允许你使用单个闭包快速定义身份验证过程。
首先,请在 AuthServiceProvider
的 boot
方法中调用 Auth::viaRequest
方法。viaRequest
方法接受身份验证驱动程序名称作为其第一个参数。此名称可以是描述你的自定义守卫的任何字符串。传递给方法的第二个参数应该是一个闭包,该闭包接收传入的 HTTP 请求并返回用户实例,或者,如果身份验证失败返回 null
:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* 启动任何应用服务。
*/
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
facade 的 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
{
// ...
/**
* 启动任何应用服务。
*/
public function boot(): void
{
Auth::provider('mongo', function (Application $app, array $config) {
// 返回 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
的数据库字段中)来检索用户。与前面的方法一样,这个方法应该返回一个拥有匹配 token 值的 Authenticatable
实现。
updateRememberToken
方法使用新的 $token
更新 $user
实例的 remember_token
。成功的「记住我」认证尝试或用户注销时会分配一个新令牌给用户。
尝试使用应用进行认证时,retrieveByCredentials
方法接收传递给 Auth::attempt
方法的凭证数组。然后,该方法应该 「查询」底层持久存储中匹配这些凭证的用户。通常,这个方法会执行带有「where」条件的查询,搜索一个「username」 匹配 $credentials['username']
值的用户记录。该方法应返回一个 Authenticatable
的实现。这个方法不应该尝试做任何密码验证或身份认证。
validateCredentials
方法应比较给定的 $user
与 $credentials
以认证用户。例如,通常这个方法会使用 Hash::check
方法比较 $user->getAuthPassword()
的值与 $credentials['password']
的值。这个方法应返回 true
或 false
表明密码是否有效。
rehashPasswordIfRequired
方法应该在需要时重新 hash 给定的 $user
的密码(如果支持)。例如,通常这个方法会使用 Hash::needsRehash
方法确定 $credentials['password']
的值是否需要重新 hash 。如果密码需要重新 hash ,方法应使用 Hash::make
方法重新 hash 密码,并更新底层持久存储中的用户记录。
用户认证契约
现在我们已经探讨了 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。可以通过应用程序的 config/hashing.php
配置文件或 BCRYPT_ROUNDS
环境变量来调整 bcrypt 哈希的「工作因子」。
通常,随着 CPU / GPU 处理能力的增加,bcrypt 的工作因子应该随时间增加。如果你提高了应用程序的 bcrypt 工作因子,Laravel 将优雅地并自动地重新 hash 用户密码,因为用户通过 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 |
原文地址:cndocs/11.x/au...
译文地址:cndocs/11.x/au...