用户认证
这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。
身份认证
介绍
许多 Web 应用程序都提供了一种让用户对应用程序进行身份验证并“登录”的方式。在 Web 应用程序中实现此功能可能是一项复杂且具有潜在风险的工作。因此,Laravel 致力于为你提供快速、安全且简单实现身份认证所需的工具。
Laravel 的身份认证功能核心由 “guards(守卫)” 和 “providers(用户提供器)” 组成。
Guard 定义了每个请求中用户的认证方式。例如,Laravel 自带一个 session Guard,它通过会话存储和 Cookie 来维护用户状态。
Provider 定义了如何从持久化存储中获取用户。Laravel 支持通过 Eloquent 和数据库查询构建器来获取用户。不过,你也可以根据应用程序的需求自由定义额外的 Provider。
你的应用程序身份认证配置文件位于 config/auth.php。该文件包含多个详细说明的配置选项,用于调整 Laravel 身份认证服务的行为。
[!注意]
Guard(守卫)和 Provider(用户提供器)不应与“角色(roles)”和“权限(permissions)”混淆。有关如何通过权限对用户操作进行授权的更多信息,请参阅 授权 文档。
入门套件
想快速开始吗?在一个全新的 Laravel 应用程序中安装一个 Laravel 应用程序入门套件。完成数据库迁移后,在浏览器中访问 /register 或应用程序中配置的其他任意 URL。入门套件会帮你搭建完整的身份认证系统!
即使你最终的 Laravel 应用程序选择不使用入门套件,安装一个 入门套件 仍然是学习如何在实际 Laravel 项目中实现 Laravel 全部身份认证功能的绝佳机会。 由于 Laravel 入门套件已经为你提供了身份认证控制器、路由和视图,你可以查看这些文件中的代码,从而了解 Laravel 的身份认证功能是如何实现的。
数据库注意事项
默认情况下,Laravel 会在你的 app/Models 目录中包含一个 App\Models\User Eloquent 模型。该模型可以与默认的 Eloquent 身份认证驱动一起使用。
如果你的应用程序没有使用 Eloquent,则可以使用基于 Laravel 查询构建器的 database 身份认证 Provider。
如果你的应用程序使用 MongoDB,请查看 MongoDB 官方的 Laravel 用户身份认证文档。
在为 App\Models\User 模型构建数据库结构时,请确保密码字段(password column)的长度至少为 60 个字符。当然,新 Laravel 应用程序中自带的 users 表迁移文件已经创建了一个超过此长度限制的字段。
此外,你还应该确保 users 表(或等效的数据表)包含一个可为空(nullable)的字符串类型 remember_token 字段,并且长度为 100 个字符。该字段用于存储选择“记住我(remember me)”选项的用户登录令牌。
同样,新 Laravel 应用程序中默认包含的 users 表迁移文件已经包含了该字段。
生态系统概览
Laravel 提供了多个与身份认证相关的软件包。在继续之前,我们先了解 Laravel 身份认证生态系统的整体情况,并讨论每个软件包的设计用途。
首先,我们来了解身份认证的工作方式。
当使用 Web 浏览器时,用户会通过登录表单提交用户名和密码。如果这些凭证正确,应用程序会将已认证用户的信息存储到用户的 会话(session) 中。
发送给浏览器的 Cookie 中包含会话 ID,因此后续向应用程序发起的请求可以通过该 ID 将用户关联到正确的会话。
当应用程序接收到会话 Cookie 后,会根据会话 ID 获取会话数据,发现身份认证信息已经存储在会话中,然后将该用户视为“已认证”。
当远程服务需要进行身份认证以访问 API 时,通常不会使用 Cookie 进行认证,因为远程服务不存在 Web 浏览器环境。
相反,远程服务会在每次请求中向 API 发送一个 API Token。
应用程序可以将收到的 Token 与数据库中有效的 API Token 表进行验证,并将该请求“认证”为由与该 API Token 关联的用户所发起。
Laravel 内置的浏览器身份认证服务
Laravel 包含了内置的身份认证和会话服务,这些服务通常通过 Auth 和 Session 门面进行访问。
这些功能为从 Web 浏览器发起的请求提供基于 Cookie 的身份认证方式。它们提供了一些方法,可以让你验证用户凭据并对用户进行身份认证。
此外,这些服务还会自动在用户的会话中存储正确的身份认证数据,并为用户签发会话 Cookie。
本文档中将详细介绍如何使用这些服务。
应用程序入门套件
正如本文档中所讨论的,你可以手动与这些身份认证服务进行交互,以构建应用程序自己的身份认证层。
不过,为了帮助你更快速地开始开发,我们发布了免费的入门套件,它们提供了完整身份认证层的强大且现代化的脚手架代码。
Laravel 的 API 身份认证服务
Laravel 提供了两个可选的软件包,用于帮助你管理 API Token,并对使用 API Token 发起的请求进行身份认证:Passport 和 Sanctum。
请注意,这些库与 Laravel 内置的基于 Cookie 的身份认证库并不是互相排斥的。
这些库主要用于 API Token 身份认证,而 Laravel 内置的身份认证服务则主要用于基于 Cookie 的浏览器身份认证。
许多应用程序会同时使用 Laravel 内置的基于 Cookie 的身份认证服务,以及 Laravel 提供的其中一个 API 身份认证软件包。
Passport
Passport 是一个 OAuth2 身份认证提供器,它提供了多种 OAuth2 “授权类型(grant types)”,允许你签发各种类型的 Token。
总体来说,这是一个功能强大但较为复杂的 API 身份认证软件包。
不过,大多数应用程序并不需要 OAuth2 规范所提供的复杂功能,因为这些功能可能会让用户和开发者都感到困惑。
此外,开发者过去经常对如何使用 Passport 这类 OAuth2 身份认证提供器为 SPA(单页应用程序)或移动应用程序进行身份认证感到困惑。
Sanctum
针对 OAuth2 的复杂性以及开发者在使用过程中的困惑,我们着手构建一个更简单、更精简的身份认证软件包,它能够同时处理来自 Web 浏览器的第一方 Web 请求,以及通过 Token 进行的 API 请求。
这一目标通过发布 Laravel Sanctum 得以实现。
对于以下类型的应用程序,Laravel Sanctum 应被视为首选并推荐使用的身份认证软件包:
- 除 API 外,还提供第一方 Web 用户界面的应用程序;
- 后端 Laravel 应用程序之外,独立存在的单页应用程序(SPA)驱动的应用程序;
- 提供移动客户端的应用程序。
Laravel Sanctum 是一个混合型 Web / API 身份认证软件包,可以管理应用程序完整的身份认证流程。
这是因为,当基于 Sanctum 的应用程序接收到请求时,Sanctum 首先会判断该请求是否包含一个引用已认证会话的会话 Cookie。
Sanctum 通过调用 Laravel 内置的身份认证服务来完成这一判断,我们之前已经介绍过这些服务。
如果请求不是通过会话 Cookie 进行身份认证的,Sanctum 会检查请求中是否包含 API Token。
如果存在 API Token,Sanctum 将使用该 Token 对请求进行身份认证。
要了解此过程的更多信息,请参考 Sanctum 的 "工作原理" 文档。
总结与技术栈选择
总结来说,如果你的应用程序将通过浏览器访问,并且你正在构建一个单体 Laravel 应用程序,那么你的应用程序将使用 Laravel 内置的身份认证服务。
其次,如果你的应用程序提供一个供第三方使用的 API,那么你需要在 Passport 或 Sanctum 之间进行选择,为你的应用程序提供 API Token 身份认证功能。
通常情况下,只要条件允许,应优先选择 Sanctum,因为它为 API 身份认证、SPA 身份认证以及移动端身份认证提供了一个简单而完整的解决方案,同时还支持“作用域(scopes)”或“能力(abilities)”功能。
如果你正在构建一个由 Laravel 后端驱动的单页应用程序(SPA),你应该使用 Laravel Sanctum。
使用 Sanctum 时,你可以选择以下两种方式之一:
- 手动实现自己的后端身份认证路由;
- 使用 Laravel Fortify 作为无头(Headless)身份认证后端服务,它可以为注册、密码重置、邮箱验证等功能提供路由和控制器。
当你的应用程序确实需要 OAuth2 规范提供的全部功能时,可以选择 Passport。
另外,如果你希望快速开始开发,我们很高兴向你推荐我们的应用程序入门套件,它可以帮助你快速创建一个新的 Laravel 应用程序,并且该应用程序已经使用了我们推荐的 Laravel 内置身份认证服务技术栈。
身份认证快速入门
[!警告]
本部分文档介绍如何通过 Laravel 应用程序入门套件 对用户进行身份认证,其中包含帮助你快速开始开发的 UI 脚手架。如果你希望直接集成 Laravel 的身份认证系统,请查看手动认证用户相关文档。
安装入门套件
首先,你应该安装 Laravel 应用程序入门套件。
我们的入门套件提供了设计精美的起点方案,可以帮助你在全新的 Laravel 应用程序中快速集成身份认证功能。
获取已认证用户
在通过入门套件创建应用程序,并允许用户注册以及完成身份认证后,你通常需要与当前已认证用户进行交互。
在处理传入请求时,你可以通过 Auth 门面的 user 方法访问已认证用户:
use Illuminate\Support\Facades\Auth;
// 获取当前已认证用户...
$user = Auth::user();
// 获取当前已认证用户的 ID...
$id = Auth::id();
或者,当用户完成身份认证后,你也可以通过 Illuminate\Http\Request 实例访问已认证用户。
请记住,类型提示(type-hinted)的类会自动注入到你的控制器方法中。
通过对 Illuminate\Http\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 门面的 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): void {
$middleware->redirectGuestsTo('/login');
// 使用闭包...
$middleware->redirectGuestsTo(fn (Request $request) => route('login'));
})
重定向已认证用户
当 guest 中间件检测到用户已经完成身份认证时,它会将用户重定向到 dashboard 或 home 命名路由。
你可以通过应用程序 bootstrap/app.php 文件中的 redirectUsersTo 方法修改此行为:
use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware): void {
$middleware->redirectUsersTo('/panel');
// 使用闭包...
$middleware->redirectUsersTo(fn (Request $request) => route('panel'));
})
指定 Guard
当你将 auth 中间件附加到路由时,也可以指定用于认证用户的“Guard(守卫)”。
指定的 Guard 应该对应 auth.php 配置文件中 guards 数组里的某个键:
Route::get('/flights', function () {
// 只有已认证用户才能访问该路由...
})->middleware('auth:admin');
登录限流
如果你正在使用我们的某个应用程序入门套件,登录尝试的频率限制会自动启用。默认情况下,如果用户多次尝试登录但未能提供正确的凭据,系统会禁止该用户在一分钟内再次登录。该限流机制会根据用户的用户名 / 邮箱地址以及 IP 地址进行唯一限制。
[!注意]
如果你希望限制应用程序中其他路由的访问频率,请查看限流文档。
手动认证用户
你不需要必须使用 Laravel 应用程序入门套件 中包含的身份认证脚手架。
如果你选择不使用这些脚手架,那么你需要直接使用 Laravel 的身份认证类来管理用户身份认证。
不用担心,这非常简单!
我们将通过 Auth 门面访问 Laravel 的身份认证服务,因此需要确保在类的顶部引入 Auth 门面。
接下来,让我们看看 attempt 方法。
attempt 方法通常用于处理应用程序“登录”表单提交的身份认证请求。
如果身份认证成功,你应该重新生成用户的 会话(session),以防止会话固定攻击(session fixation):
<?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' => '提供的凭据与我们的记录不匹配。',
])->onlyInput('email');
}
}
attempt 方法的第一个参数接收一个键 / 值对数组。数组中的值将用于从你的数据库表中查找用户。
因此,在上面的示例中,系统会通过 email 字段的值获取用户。如果找到用户,数据库中存储的哈希密码将会与通过数组传递给该方法的 password 值进行比较。你不应该对传入请求中的 password 值进行哈希处理,因为框架会在将该值与数据库中的哈希密码进行比较之前自动完成哈希处理。如果两个哈希密码匹配,则会为该用户启动一个已认证会话。
请记住,Laravel 的身份认证服务会根据你的身份认证 Guard 中的 Provider(用户提供器) 配置,从数据库中获取用户。在默认的 config/auth.php 配置文件中,指定使用 Eloquent 用户 Provider,并且配置其在获取用户时使用 App\Models\User 模型。你可以根据应用程序的需求,在配置文件中修改这些配置项。如果身份认证成功,attempt 方法将返回 true;否则将返回 false。Laravel 重定向器提供的 intended 方法会将用户重定向到他们在被身份认证中间件拦截之前尝试访问的 URL。如果目标地址不可用,可以向该方法提供一个备用 URI。
指定额外条件
如果需要,你还可以在身份认证查询中添加额外的查询条件,除了用户的邮箱和密码之外。实现这一功能非常简单,只需要将额外的查询条件添加到传递给 attempt 方法的数组中即可。例如,我们可以验证用户是否被标记为“已启用(active)”:
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// 身份认证成功...
}
对于复杂的查询条件,你可以在凭据数组中提供一个闭包(Closure)。该闭包会接收查询实例作为参数,从而允许你根据应用程序的需求自定义查询:
use Illuminate\Database\Eloquent\Builder;
if (Auth::attempt([
'email' => $email,
'password' => $password,
fn (Builder $query) => $query->has('activeSubscription'),
])) {
// 身份认证成功...
}
[!警告]
在这些示例中,
attemptWhen 方法接收一个闭包(Closure)作为第二个参数,它可以用于在真正对用户进行身份认证之前,对潜在用户进行更深入的检查。该闭包会接收潜在用户对象,并且应该返回 true 或 false,用于指示该用户是否可以通过身份认证:
if (Auth::attemptWhen([
'email' => $email,
'password' => $password,
], function (User $user) {
return $user->isNotBanned();
})) {
// 身份认证成功...
}
访问指定的 Guard 实例
通过 Auth 门面的 guard 方法,你可以指定在用户身份认证时需要使用的 Guard 实例。
这允许你在应用程序的不同部分中,使用完全独立的可认证模型(Authenticatable Model)或用户数据表来管理身份认证。传递给 guard 方法的 Guard 名称应该对应 auth.php 配置文件中配置的某个 Guard:
if (Auth::guard('admin')->attempt($credentials)) {
// ...
}
记住用户
许多 Web 应用程序都会在登录表单中提供一个“记住我(remember me)”复选框。如果你希望在应用程序中提供“记住我”功能,可以将一个布尔值作为 attempt 方法的第二个参数传递。当该值为 true 时,Laravel 会保持用户持续登录状态,直到用户手动退出登录。你的 users 表必须包含一个字符串类型的 remember_token 字段,该字段用于存储“记住我”Token。新 Laravel 应用程序中包含的 users 表迁移文件已经默认包含该字段:
use Illuminate\Support\Facades\Auth;
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
// 用户正在被记住...
}
如果你的应用程序提供“记住我(remember me)”功能,你可以使用 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 方法之前指定身份认证 Guard:
Auth::guard('admin')->login($user);
根据用户 ID 认证用户
如果你希望通过用户数据库记录的主键对用户进行身份认证,可以使用 loginUsingId 方法。该方法接收你希望进行身份认证的用户主键:
Auth::loginUsingId(1);
你可以向 loginUsingId 方法的 remember 参数传递一个布尔值。该值用于表示当前已认证会话是否需要启用“记住我”功能。请记住,这意味着该会话将会一直保持认证状态,直到用户手动退出应用程序:
Auth::loginUsingId(1, remember: true);
仅认证用户一次
你可以使用 once 方法让用户在单个请求中完成应用程序身份认证。调用该方法时,不会使用任何会话或 Cookie,并且不会触发 Login 事件:
if (Auth::once($credentials)) {
// ...
}
HTTP 基本认证
HTTP 基本认证 提供了一种快速认证应用程序用户的方式,无需创建专门的“登录”页面。开始使用时,只需要将 auth.basic 中间件附加到路由即可。auth.basic 中间件已经包含在 Laravel 框架中,因此你无需自行定义:
Route::get('/profile', function () {
// 只有已认证用户才能访问该路由...
})->middleware('auth.basic');
将中间件附加到路由后,当你通过浏览器访问该路由时,系统会自动提示你输入身份认证凭据。默认情况下,auth.basic 中间件会假设你的 users 数据表中的 email 字段就是用户的“用户名”。
关于 FastCGI 的说明
如果你使用 PHP FastCGI 和 Apache 来运行 Laravel 应用程序,HTTP 基本认证可能无法正常工作。
要解决这些问题,可以将以下代码添加到应用程序的 .htaccess 文件中:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
无状态 HTTP 基本认证
你也可以使用 HTTP 基本认证,而无需在会话中设置用户标识 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 门面提供的 logout 方法。该方法会从用户的会话中移除身份认证信息,因此后续请求将不再被视为已认证请求。除了调用 logout 方法之外,还建议使用户的会话失效,并重新生成其 CSRF Token。用户退出登录后,通常会将用户重定向到应用程序的根路径:
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('/');
}
使其他设备上的会话失效
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 方法后,用户在其他设备上的会话将会被完全失效。这意味着用户将从之前通过所有 Guard 完成身份认证的会话中全部“退出登录”。
密码确认
在构建应用程序时,你可能偶尔会遇到一些操作,需要用户在执行操作之前确认密码,或者在用户被重定向到应用程序的敏感区域之前确认密码。Laravel 提供了内置中间件,使这一过程变得非常简单。
实现此功能需要定义两个路由:
- 一个路由用于显示要求用户确认密码的页面;
- 另一个路由用于确认密码是否有效,并将用户重定向到其目标页面。
[!注意]
以下文档介绍如何直接集成 Laravel 的密码确认功能。但是,如果你希望更快速地开始使用,Laravel 应用程序入门套件已经包含了对该功能的支持!
Configuration
After confirming their password, a user will not be asked to confirm their password again for three hours. However, you may configure the length of time before the user is re-prompted for their password by changing the value of the password_timeout configuration value within your application's config/auth.php configuration file.
Routing
The Password Confirmation Form
First, we will define a route to display a view that requests the user to confirm their password:
Route::get('/confirm-password', function () {
return view('auth.confirm-password');
})->middleware('auth')->name('password.confirm');
As you might expect, the view that is returned by this route should have a form containing a password field. In addition, feel free to include text within the view that explains that the user is entering a protected area of the application and must confirm their password.
Confirming the Password
Next, we will define a route that will handle the form request from the "confirm password" view. This route will be responsible for validating the password and redirecting the user to their intended destination:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
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']);
Before moving on, let's examine this route in more detail. First, the request's password field is determined to actually match the authenticated user's password. If the password is valid, we need to inform Laravel's session that the user has confirmed their password. The passwordConfirmed method will set a timestamp in the user's session that Laravel can use to determine when the user last confirmed their password. Finally, we can redirect the user to their intended destination.
Protecting Routes
You should ensure that any route that performs an action which requires recent password confirmation is assigned the password.confirm middleware. This middleware is included with the default installation of Laravel and will automatically store the user's intended destination in the session so that the user may be redirected to that location after confirming their password. After storing the user's intended destination in the session, the middleware will redirect the user to the password.confirm named route:
Route::get('/settings', function () {
// ...
})->middleware(['password.confirm']);
Route::post('/settings', function () {
// ...
})->middleware(['password.confirm']);
Adding Custom Guards
You may define your own authentication guards using the extend method on the Auth facade. You should place your call to the extend method within a service provider. Since Laravel already ships with an AppServiceProvider, we can place the code in that provider:
<?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']));
});
}
}
As you can see in the example above, the callback passed to the extend method should return an implementation of Illuminate\Contracts\Auth\Guard. This interface contains a few methods you will need to implement to define a custom guard. Once your custom guard has been defined, you may reference the guard in the guards configuration of your auth.php configuration file:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
Closure Request Guards
The simplest way to implement a custom, HTTP request based authentication system is by using the Auth::viaRequest method. This method allows you to quickly define your authentication process using a single closure.
To get started, call the Auth::viaRequest method within the boot method of your application's AppServiceProvider. The viaRequest method accepts an authentication driver name as its first argument. This name can be any string that describes your custom guard. The second argument passed to the method should be a closure that receives the incoming HTTP request and returns a user instance or, if authentication fails, 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();
});
}
Once your custom authentication driver has been defined, you may configure it as a driver within the guards configuration of your auth.php configuration file:
'guards' => [
'api' => [
'driver' => 'custom-token',
],
],
Finally, you may reference the guard when assigning the authentication middleware to a route:
Route::middleware('auth:api')->group(function () {
// ...
});
Adding Custom User Providers
If you are not using a traditional relational database to store your users, you will need to extend Laravel with your own authentication user provider. We will use the provider method on the Auth facade to define a custom user provider. The user provider resolver should return an implementation of 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'));
});
}
}
After you have registered the provider using the provider method, you may switch to the new user provider in your auth.php configuration file. First, define a provider that uses your new driver:
'providers' => [
'users' => [
'driver' => 'mongo',
],
],
Finally, you may reference this provider in your guards configuration:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
The User Provider Contract
Illuminate\Contracts\Auth\UserProvider implementations are responsible for fetching an Illuminate\Contracts\Auth\Authenticatable implementation out of a persistent storage system, such as MySQL, MongoDB, etc. These two interfaces allow the Laravel authentication mechanisms to continue functioning regardless of how the user data is stored or what type of class is used to represent the authenticated user:
Let's take a look at the Illuminate\Contracts\Auth\UserProvider contract:
<?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);
}
The retrieveById function typically receives a key representing the user, such as an auto-incrementing ID from a MySQL database. The Authenticatable implementation matching the ID should be retrieved and returned by the method.
The retrieveByToken function retrieves a user by their unique $identifier and "remember me" $token, typically stored in a database column like remember_token. As with the previous method, the Authenticatable implementation with a matching token value should be returned by this method.
The updateRememberToken method updates the $user instance's remember_token with the new $token. A fresh token is assigned to users on a successful "remember me" authentication attempt or when the user is logging out.
The retrieveByCredentials method receives the array of credentials passed to the Auth::attempt method when attempting to authenticate with an application. The method should then "query" the underlying persistent storage for the user matching those credentials. Typically, this method will run a query with a "where" condition that searches for a user record with a "username" matching the value of $credentials['username']. The method should return an implementation of Authenticatable. This method should not attempt to do any password validation or authentication.
The validateCredentials method should compare the given $user with the $credentials to authenticate the user. For example, this method will typically use the Hash::check method to compare the value of $user->getAuthPassword() to the value of $credentials['password']. This method should return true or false indicating whether the password is valid.
The rehashPasswordIfRequired method should rehash the given $user's password if required and supported. For example, this method will typically use the Hash::needsRehash method to determine if the $credentials['password'] value needs to be rehashed. If the password needs to be rehashed, the method should use the Hash::make method to rehash the password and update the user's record in the underlying persistent storage.
The Authenticatable Contract
Now that we have explored each of the methods on the UserProvider, let's take a look at the Authenticatable contract. Remember, user providers should return implementations of this interface from the retrieveById, retrieveByToken, and retrieveByCredentials methods:
<?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();
}
This interface is simple. The getAuthIdentifierName method should return the name of the "primary key" column for the user and the getAuthIdentifier method should return the "primary key" of the user. When using a MySQL back-end, this would likely be the auto-incrementing primary key assigned to the user record. The getAuthPasswordName method should return the name of the user's password column. The getAuthPassword method should return the user's hashed password.
This interface allows the authentication system to work with any "user" class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes an App\Models\User class in the app/Models directory which implements this interface.
Automatic Password Rehashing
Laravel's default password hashing algorithm is bcrypt. The "work factor" for bcrypt hashes can be adjusted via your application's config/hashing.php configuration file or the BCRYPT_ROUNDS environment variable.
Typically, the bcrypt work factor should be increased over time as CPU / GPU processing power increases. If you increase the bcrypt work factor for your application, Laravel will gracefully and automatically rehash user passwords as users authenticate with your application via Laravel's starter kits or when you manually authenticate users via the attempt method.
Typically, automatic password rehashing should not disrupt your application; however, you may disable this behavior by publishing the hashing configuration file:
php artisan config:publish hashing
Once the configuration file has been published, you may set the rehash_on_login configuration value to false:
'rehash_on_login' => false,
Events
Laravel dispatches a variety of events during the authentication process. You may define listeners for any of the following events:
| Event Name | | ---------------------------------------------- | | `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` | | `Illuminate\Auth\Events\PasswordResetLinkSent` |
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
Laravel 13 中文文档
关于 LearnKu
推荐文章: