许多 Web 应用程序都为其用户提供一种通过应用程序进行身份验证并“登录”的方式。在 Web 应用程序中实现此功能可能是一个复杂且具有潜在风险的过程。因此,Laravel 致力于为您提供所需的工具,以便快速、安全、轻松地实现身份验证。
核心上,Laravel 的认证功能由“守卫”和“提供者”组成。守卫定义了用户如何针对每个请求进行认证。例如,Laravel 自带一个 session 守卫,它使用会话存储和 Cookie 来维护状态。
认证驱动定义了如何从您的持久化存储中获取用户。Laravel 内置支持使用 Eloquent 和数据库查询构建器获取用户。然而,您可以根据您的应用程序需要自由定义额外的认证驱动。
您的应用的认证配置文件位于 config/auth.php。此文件包含多个文档完善的选项,用于调整 Laravel 认证服务的行为。
[!NOTE]
守卫和提供者不应与“角色”和“权限”混淆。要了解更多关于通过权限授权用户操作的信息,请参考授权文档。
想要快速上手吗?安装一个 Laravel 应用程序入门套件 到一个全新的 Laravel 应用程序中。迁移数据库后,在浏览器中访问 /register 或分配给您应用程序的任何其他 URL。入门套件将负责搭建您的整个身份验证系统!
**即使你选择不在最终的 Laravel 应用程序中使用入门套件,安装一个 入门套件 也是一个绝佳的学习机会,可以了解如何在实际的 Laravel 项目中实现 Laravel 的所有认证功能。**由于 Laravel 入门套件为你提供了认证控制器、路由和视图,你可以检查这些文件中的代码,学习 Laravel 的认证功能是如何实现的。
默认情况下,Laravel 在你的 app/Models 目录中包含一个 App\Models\User Eloquent 模型。该模型可用于默认的 Eloquent 认证驱动。
如果你的应用没有使用 Eloquent,你可以使用 database 认证提供器,它使用 Laravel 查询构建器。如果你的应用正在使用 MongoDB,请查阅 MongoDB 官方的 Laravel 用户认证文档。
为 App\Models\User 模型构建数据库模式时,请确保密码列的长度至少为 60 个字符。当然,新 Laravel 应用程序中包含的 users 表迁移已经创建了一个长度超过此限制的列。
此外,您还应该验证您的 users (或等效)表包含一个可空、字符串类型的 remember_token 列,长度为100个字符。此列将用于存储那些在登录您的应用程序时选择“记住我”选项的用户的令牌。同样,新 Laravel 应用程序中包含的默认 users 表迁移已包含此列。
Laravel 提供多个与身份验证相关的包。在继续之前,我们将回顾 Laravel 中整体的身份验证生态系统,并讨论每个包的预期用途。
首先,考虑认证的工作原理。当使用网页浏览器时,用户将通过登录表单提供其用户名和密码。如果这些凭据正确,应用程序会将已认证用户的信息存储在用户的会话中。发给浏览器的 Cookie 包含会话 ID,以便后续对应用程序的请求可以将用户与正确的会话关联起来。收到会话 Cookie 后,应用程序将根据会话 ID 检索会话数据,注意到认证信息已存储在会话中,并将用户视为“已认证”。
当远程服务需要认证才能访问 API 时,通常不使用 cookie 进行认证,因为没有 Web 浏览器。相反,远程服务在每次请求时都会向 API 发送一个 API 令牌。应用程序可能会根据一个有效的 API 令牌表来验证传入的令牌,并“认证”该请求是由与该 API 令牌关联的用户执行的。
Laravel 包含内置的认证和会话服务,这些服务通常通过 Auth 和 Session 门面进行访问。这些功能为从 Web 浏览器发起的请求提供基于 Cookie 的认证。它们提供方法,允许你验证用户的凭据并对用户进行认证。此外,这些服务将自动在用户的会话中存储正确的认证数据,并发放用户的会话 Cookie。关于如何使用这些服务的讨论包含在本文档中。
应用程序启动套件
正如本文档所讨论的,您可以手动与这些认证服务交互,以构建您应用程序自己的认证层。然而,为了帮助您更快地入门,我们发布了免费入门套件,它们提供了整个认证层的健壮、现代的脚手架。
Laravel 提供了两个可选的包,用于帮助你管理 API 令牌并认证使用 API 令牌发出的请求:Passport 和 Sanctum。请注意,这些库和 Laravel 内置的基于 Cookie 的认证库并非相互排斥。这些库主要侧重于 API 令牌认证,而内置认证服务则侧重于基于 Cookie 的浏览器认证。许多应用程序将同时使用 Laravel 内置的基于 Cookie 的认证服务和 Laravel 的其中一个 API 认证包。
护照
Passport 是一个 OAuth2 认证提供者,提供多种 OAuth2 "授权类型",允许您颁发各种类型的令牌。总的来说,这是一个强大且复杂的包,用于 API 认证。然而,大多数应用程序不需要 OAuth2 规范提供的复杂功能,这可能让用户和开发者都感到困惑。此外,开发者一直对如何使用像 Passport 这样的 OAuth2 认证提供者认证 SPA 应用程序或移动应用程序感到困惑。
圣所
鉴于 OAuth2 的复杂性和开发者的困惑,我们着手构建一个更简单、更精简的认证包,既能处理来自 Web 浏览器的第一方 Web 请求,又能处理通过令牌进行的 API 请求。随着 Laravel Sanctum 的发布,这一目标得以实现,它应该被视为首选和推荐的认证包,适用于除了 API 之外还将提供第一方 Web UI 的应用程序,或者由独立于后端 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 令牌认证。通常情况下,应尽可能优先选择 Sanctum,因为它是一个用于 API 认证、SPA 认证和移动认证的简单、完整的解决方案,包括支持“作用域”或“能力”。
如果您正在构建一个由 Laravel 后端驱动的单页应用 (SPA),您应该使用 Laravel Sanctum。当使用 Sanctum 时,您将需要 手动实现自己的后端认证路由 或利用 Laravel Fortify 作为一个无头认证后端服务,它提供了注册、密码重置、邮箱验证等功能的路由和控制器。
当您的应用程序绝对需要 OAuth2 规范提供的所有功能时,可以选择 Passport。
并且,如果您想快速上手,我们很高兴推荐我们的应用入门套件作为一种快速启动新的 Laravel 应用的方式,该应用已使用我们首选的 Laravel 内置认证服务组成的认证堆栈。
[!WARNING]
文档的这一部分讨论了通过 Laravel 应用入门套件 认证用户,其中包含了 UI 脚手架,可帮助您快速入门。如果您想直接与 Laravel 的认证系统集成,请查阅关于 手动认证用户 的文档。
首先,你应该安装 Laravel 应用入门套件。我们的入门套件提供了设计精美的起点,用于将认证功能集成到你的全新 Laravel 应用中。
从启动套件创建应用程序并允许用户注册和认证您的应用程序后,您通常需要与当前认证的用户进行交互。在处理传入请求时,您可以通过 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...
}[!NOTE]
尽管可以使用check方法确定用户是否已认证,但你通常会使用中间件来验证用户是否已认证,然后才允许用户访问某些路由/控制器。要了解更多信息,请查看关于 保护路由 的文档。
路由中间件 可用于仅允许已认证用户访问给定路由。Laravel 内置了一个 auth 中间件,它是 中间件别名 ,对应 Illuminate\Auth\Middleware\Authenticate 类。由于此中间件已由 Laravel 在内部进行了别名设置,所以你只需将此中间件附加到路由定义中即可:
Route::get('/flights', function () {
// Only authenticated users may access this route...
})->middleware('auth');当 auth 中间件检测到未认证用户时,它会将用户重定向到 login 命名路由。您可以使用 redirectGuestsTo 方法在您的应用程序的 bootstrap/app.php 文件中修改此行为:
use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware): void {
$middleware->redirectGuestsTo('/login');
// Using a closure...
$middleware->redirectGuestsTo(fn (Request $request) => route('login'));
})当 guest 中间件检测到已认证用户时,它会将用户重定向到 dashboard 或 home 命名路由。您可以通过修改此行为,使用 redirectUsersTo 方法在您应用程序的 bootstrap/app.php 文件中:
use Illuminate\Http\Request;
->withMiddleware(function (Middleware $middleware): void {
$middleware->redirectUsersTo('/panel');
// Using a closure...
$middleware->redirectUsersTo(fn (Request $request) => route('panel'));
})当将 auth 中间件附加到路由时,您还可以指定应使用哪个“guard”来认证用户。指定的 guard 应与您的 auth.php 配置文件中 guards 数组中的一个键相对应:
Route::get('/flights', function () {
// Only authenticated users may access this route...
})->middleware('auth:admin');如果您正在使用我们的某个应用程序启动套件,速率限制将自动应用于登录尝试。默认情况下,如果用户在多次尝试后未能提供正确的凭据,他们将在一分钟内无法登录。此限制对于用户的用户名/电子邮件地址及其IP地址是唯一的。
[!NOTE]
如果您想对应用程序中的其他路由进行速率限制,请查阅速率限制文档。
您无需使用随 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 的认证服务将根据您的认证守卫的“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])) {
// Authentication was successful...
}对于复杂的查询条件,您可以在您的凭据数组中提供一个闭包。该闭包将被调用并传入查询实例,允许您根据应用程序的需求自定义查询:
use Illuminate\Database\Eloquent\Builder;
if (Auth::attempt([
'email' => $email,
'password' => $password,
fn (Builder $query) => $query->has('activeSubscription'),
])) {
// Authentication was successful...
}[!警告]
在这些示例中,
attemptWhen 方法,它将一个闭包作为其第二个参数,可用于在实际验证用户之前对潜在用户执行更广泛的检查。该闭包接收潜在用户,并应返回 true 或 false 以指示用户是否可以被验证:
if (Auth::attemptWhen([
'email' => $email,
'password' => $password,
], function (User $user) {
return $user->isNotBanned();
})) {
// Authentication was successful...
}通过Auth facade的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);要使用用户的数据库记录主键验证用户,您可以使用 loginUsingId 方法。此方法接受您希望验证的用户的主键:
Auth::loginUsingId(1);您可以将布尔值传递给remember参数,该参数属于loginUsingId方法。此值表示是否希望为已认证的会话启用“记住我”功能。请记住,这意味着该会话将无限期地保持认证状态,或直到用户手动从应用程序注销:
Auth::loginUsingId(1, remember: true);您可以使用 once 方法,在应用程序中对用户进行身份验证,仅限单个请求。调用此方法时,不会使用会话或 Cookie,并且 Login 事件不会被分发:
if (Auth::once($credentials)) {
// ...
}HTTP 基本认证 提供了一种快速验证应用程序用户的方法,而无需设置专门的“登录”页面。要开始使用,将 auth.basic 中间件 附加到路由。 auth.basic 中间件已包含在 Laravel 框架中,因此您无需定义它:
Route::get('/profile', function () {
// Only authenticated users may access this route...
})->middleware('auth.basic');一旦中间件已绑定到路由, 您在浏览器中访问该路由时将自动收到凭据提示。 默认情况下, auth.basic 中间件将假定您的 users 数据库表中的 email 列是用户的“用户名”。
如果您正在使用 PHP FastCGI 和 Apache 来为您的 Laravel 应用程序提供服务,HTTP 基本认证可能无法正常工作。为纠正这些问题,以下行可以添加到您的应用程序的 .htaccess 文件中:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]您也可以使用 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
{
/**
* 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 中间件已包含在应接收会话认证的路由中。通常,您应该将此中间件放置在路由组定义上,以便它可以应用于您应用程序的大多数路由。默认情况下,AuthenticateSession 中间件可以使用 auth.session 中间件别名附加到路由:
Route::middleware(['auth', 'auth.session'])->group(function () {
Route::get('/', function () {
// ...
});
});然后,您可以使用 Auth 门面提供的 logoutOtherDevices 方法。此方法要求用户确认其当前密码,您的应用程序应通过输入表单接受此密码:
use Illuminate\Support\Facades\Auth;
Auth::logoutOtherDevices($currentPassword);当 logoutOtherDevices 方法被调用时,用户的其他会话将完全失效,这意味着他们将从之前通过身份验证的所有守卫中“注销”。
在构建应用程序时,您可能偶尔会遇到一些操作,这些操作要求用户在执行该操作之前,或者在用户被重定向到应用程序的敏感区域之前,确认其密码。Laravel 包含了内置的中间件,可以让这个过程变得轻而易举。实现此功能将要求您定义两条路由:一条路由用于显示一个视图,要求用户确认其密码;另一条路由用于确认密码是否有效,并将用户重定向到他们预期的目的地。
[!NOTE]
以下文档讨论了如何直接集成 Laravel 的密码确认功能;然而,如果您想更快地开始,[Laravel 应用程序入门套件](/docs/laravel/12.x/zh-cn/starter-kits) 包含了对此功能的支持!
用户在确认密码后,三小时内不会再次被要求确认密码。但是,你可以通过修改应用程序 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;
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 () {
// ...
});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'));
});
}
}在您使用 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 方法应重新哈希给定 $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\User 类在 app/Models 目录中其实现了此接口。
Laravel 的默认密码哈希算法是 bcrypt。bcrypt 哈希的“工作因子”可以通过您应用的 config/hashing.php 配置文件或 BCRYPT_ROUNDS 环境变量进行调整。
通常,bcrypt 工作因子应该随着 CPU / GPU 处理能力的提升而逐渐增加。如果你为你的应用程序增加了 bcrypt 工作因子,Laravel 将优雅地自动重新哈希用户密码 当用户通过 Laravel 的入门套件与你的应用程序进行身份验证时或者当你通过 attempt 方法手动验证用户时。
通常情况下,自动密码重哈希不应中断您的应用程序;但是,您可以通过发布 hashing 配置文件来禁用此行为:
php artisan config:publish hashing一旦配置文件发布后,您就可以将 rehash_on_login 配置值设置为 false:
'rehash_on_login' => false,Laravel 会在认证过程中分发各种 事件。你可以 定义监听器 来监听以下任何事件:
| 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 |