Skip to content
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

认证 ​

介绍 ​

注意

想快速开始吗? 安装 laravel/ui (1.0) Composer 包并在一个新的 Laravel 应用中运行 php artisan ui vue --auth。迁移数据库后,导航到 http://your-app.test/register 或分配给应用的其他 URL。这些命令将为您搭建整个认证系统的脚手架!

Laravel 使实现认证变得非常简单。实际上,几乎所有的配置都为您准备好了。认证配置文件位于 config/auth.php,其中包含了几个用于调整认证服务行为的详细选项。

在其核心,Laravel 的认证功能由“守卫”和“提供者”组成。守卫定义了用户如何为每个请求进行认证。例如,Laravel 附带一个 session 守卫,它使用会话存储和 cookies 来维护状态。

提供者定义了如何从持久存储中检索用户。Laravel 支持使用 Eloquent 和数据库查询构建器检索用户。不过,您可以根据应用的需要定义其他提供者。

如果现在听起来有些困惑,不用担心!许多应用程序永远不需要修改默认的认证配置。

数据库注意事项 ​

默认情况下,Laravel 在您的 app 目录中包含一个 App\User Eloquent 模型。此模型可以与默认的 Eloquent 认证驱动程序一起使用。如果您的应用程序不使用 Eloquent,您可以使用 database 认证驱动程序,该驱动程序使用 Laravel 查询构建器。

在为 App\User 模型构建数据库架构时,请确保密码列至少为 60 个字符长。保持默认的字符串列长度为 255 个字符是一个不错的选择。

此外,您应验证 users(或等效)表包含一个可为空的、长度为 100 个字符的字符串 remember_token 列。此列将用于存储选择“记住我”选项的用户的令牌。

认证快速入门 ​

Laravel 附带几个预构建的认证控制器,位于 App\Http\Controllers\Auth 命名空间中。RegisterController 处理新用户注册,LoginController 处理认证,ForgotPasswordController 处理发送重置密码链接的电子邮件,ResetPasswordController 包含重置密码的逻辑。每个控制器都使用一个 trait 来包含其必要的方法。对于许多应用程序,您根本不需要修改这些控制器。

路由 ​

Laravel 的 laravel/ui 包提供了一种快速的方法来使用几个简单的命令为认证搭建所有需要的路由和视图:

php
composer require laravel/ui "^1.0" --dev

php artisan ui vue --auth

此命令应在新应用程序上使用,并将安装一个布局视图、注册和登录视图,以及所有认证端点的路由。还将生成一个 HomeController 来处理登录后的请求到应用程序的仪表板。

注意

如果您的应用程序不需要注册,您可以通过删除新创建的 RegisterController 并修改您的路由声明来禁用它:Auth::routes(['register' => false]);。

创建包含认证的应用程序 ​

如果您正在启动一个全新的应用程序并希望包含认证脚手架,您可以在创建应用程序时使用 --auth 指令。此命令将创建一个包含所有认证脚手架编译和安装的新应用程序:

php
laravel new blog --auth

视图 ​

如前一节所述,laravel/ui 包的 php artisan ui vue --auth 命令将创建您需要的所有认证视图,并将它们放置在 resources/views/auth 目录中。

ui 命令还将创建一个 resources/views/layouts 目录,其中包含应用程序的基本布局。所有这些视图都使用 Bootstrap CSS 框架,但您可以根据需要自由定制它们。

认证 ​

现在您已经为包含的认证控制器设置了路由和视图,您可以为应用程序注册和认证新用户了!您可以在浏览器中访问您的应用程序,因为认证控制器已经包含了通过其 trait 认证现有用户并将新用户存储到数据库中的逻辑。

路径定制 ​

当用户成功认证后,他们将被重定向到 /home URI。您可以使用在 RouteServiceProvider 中定义的 HOME 常量来自定义认证后的重定向路径:

php
public const HOME = '/home';

如果您需要对用户认证后的响应进行更强大的定制,Laravel 提供了一个空的 authenticated(Request $request, $user) 方法,您可以根据需要覆盖它:

php
/**
 * 用户已认证。
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  mixed  $user
 * @return mixed
 */
protected function authenticated(Request $request, $user)
{
    return response([
        //
    ]);
}

用户名定制 ​

默认情况下,Laravel 使用 email 字段进行认证。如果您想自定义此字段,可以在 LoginController 上定义一个 username 方法:

php
public function username()
{
    return 'username';
}

守卫定制 ​

您还可以自定义用于认证和注册用户的“守卫”。要开始,请在 LoginController、RegisterController 和 ResetPasswordController 上定义一个 guard 方法。该方法应返回一个守卫实例:

php
use Illuminate\Support\Facades\Auth;

protected function guard()
{
    return Auth::guard('guard-name');
}

验证/存储定制 ​

要修改新用户注册时应用程序所需的表单字段,或自定义新用户如何存储到数据库中,您可以修改 RegisterController 类。此类负责验证和创建应用程序的新用户。

RegisterController 的 validator 方法包含应用程序新用户的验证规则。您可以根据需要自由修改此方法。

RegisterController 的 create 方法负责使用 Eloquent ORM 在数据库中创建新的 App\User 记录。您可以根据数据库的需要自由修改此方法。

检索已认证的用户 ​

您可以通过 Auth facade 访问已认证的用户:

php
use Illuminate\Support\Facades\Auth;

// 获取当前认证的用户...
$user = Auth::user();

// 获取当前认证用户的 ID...
$id = Auth::id();

或者,一旦用户认证,您可以通过 Illuminate\Http\Request 实例访问已认证的用户。请记住,类型提示的类将自动注入到您的控制器方法中:

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProfileController extends Controller
{
    /**
     * 更新用户的个人资料。
     *
     * @param  Request  $request
     * @return Response
     */
    public function update(Request $request)
    {
        // $request->user() 返回已认证用户的实例...
    }
}

确定当前用户是否已认证 ​

要确定用户是否已登录到您的应用程序,您可以使用 Auth facade 上的 check 方法,如果用户已认证,该方法将返回 true:

php
use Illuminate\Support\Facades\Auth;

if (Auth::check()) {
    // 用户已登录...
}

注意

即使可以使用 check 方法确定用户是否已认证,您通常会使用中间件来验证用户在允许用户访问某些路由/控制器之前是否已认证。要了解更多信息,请查看有关保护路由的文档。

保护路由 ​

路由中间件 可用于仅允许已认证的用户访问给定路由。Laravel 附带一个 auth 中间件,定义在 Illuminate\Auth\Middleware\Authenticate 中。由于此中间件已在您的 HTTP 内核中注册,您只需将中间件附加到路由定义即可:

php
Route::get('profile', function () {
    // 只有已认证的用户可以进入...
})->middleware('auth');

如果您使用 控制器,您可以从控制器的构造函数中调用 middleware 方法,而不是直接在路由定义中附加它:

php
public function __construct()
{
    $this->middleware('auth');
}

重定向未认证的用户 ​

当 auth 中间件检测到未授权的用户时,它将用户重定向到 login 命名路由。您可以通过更新 app/Http/Middleware/Authenticate.php 文件中的 redirectTo 函数来修改此行为:

php
/**
 * 获取用户应重定向到的路径。
 *
 * @param  \Illuminate\Http\Request  $request
 * @return string
 */
protected function redirectTo($request)
{
    return route('login');
}

指定守卫 ​

在将 auth 中间件附加到路由时,您还可以指定应使用哪个守卫来认证用户。指定的守卫应对应于 auth.php 配置文件中的 guards 数组中的一个键:

php
public function __construct()
{
    $this->middleware('auth:api');
}

密码确认 ​

有时,您可能希望要求用户在访问应用程序的特定区域之前确认其密码。例如,您可能在用户修改应用程序中的任何账单设置之前要求此操作。

为此,Laravel 提供了一个 password.confirm 中间件。将 password.confirm 中间件附加到路由将用户重定向到一个屏幕,在该屏幕上他们需要确认其密码才能继续:

php
Route::get('/settings/security', function () {
    // 用户必须确认其密码才能继续...
})->middleware(['auth', 'password.confirm']);

在用户成功确认其密码后,用户将被重定向到他们最初尝试访问的路由。默认情况下,确认密码后,用户在三小时内不必再次确认其密码。您可以使用 auth.password_timeout 配置选项自定义用户必须重新确认其密码之前的时间长度。

登录节流 ​

如果您使用 Laravel 的内置 LoginController 类,Illuminate\Foundation\Auth\ThrottlesLogins trait 将已包含在您的控制器中。默认情况下,如果用户在多次尝试后未能提供正确的凭据,则用户将无法登录一分钟。节流是针对用户的用户名/电子邮件地址及其 IP 地址的唯一性。

手动认证用户 ​

请注意,您不必使用 Laravel 附带的认证控制器。如果您选择删除这些控制器,您将需要直接使用 Laravel 认证类来管理用户认证。别担心,这很简单!

我们将通过 Auth facade 访问 Laravel 的认证服务,因此我们需要确保在类的顶部导入 Auth facade。接下来,让我们看看 attempt 方法:

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    /**
     * 处理认证尝试。
     *
     * @param  \Illuminate\Http\Request $request
     *
     * @return Response
     */
    public function authenticate(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {
            // 认证通过...
            return redirect()->intended('dashboard');
        }
    }
}

attempt 方法接受一个键/值对数组作为其第一个参数。数组中的值将用于在数据库表中查找用户。因此,在上面的示例中,用户将通过 email 列的值进行检索。如果找到用户,存储在数据库中的哈希密码将与通过数组传递给方法的 password 值进行比较。您不应哈希指定为 password 值的密码,因为框架将在将其与数据库中的哈希密码进行比较之前自动哈希该值。如果两个哈希密码匹配,将为用户启动一个已认证的会话。

如果认证成功,attempt 方法将返回 true。否则,将返回 false。

重定向器上的 intended 方法将用户重定向到他们在被认证中间件拦截之前尝试访问的 URL。可以为此方法提供一个备用 URI,以防预期的目的地不可用。

指定附加条件 ​

如果您愿意,您还可以在用户的电子邮件和密码之外向认证查询添加额外的条件。例如,我们可以验证用户是否标记为“active”:

php
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
    // 用户是活跃的,没有被暂停,并且存在。
}

注意

在这些示例中,email 不是必需的选项,它仅用作示例。您应使用与数据库中“用户名”对应的任何列名。

访问特定的守卫实例 ​

您可以使用 Auth facade 上的 guard 方法指定要使用的守卫实例。这允许您使用完全独立的可认证模型或用户表来管理应用程序的不同部分的认证。

传递给 guard 方法的守卫名称应对应于 auth.php 配置文件中配置的守卫之一:

php
if (Auth::guard('admin')->attempt($credentials)) {
    //
}

登出 ​

要将用户从应用程序中登出,您可以使用 Auth facade 上的 logout 方法。这将清除用户会话中的认证信息:

php
Auth::logout();

记住用户 ​

如果您希望在应用程序中提供“记住我”功能,可以将布尔值作为 attempt 方法的第二个参数传递,这将使用户无限期地保持认证状态,或直到他们手动登出。您的 users 表必须包含字符串 remember_token 列,该列将用于存储“记住我”令牌。

php
if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
    // 用户正在被记住...
}

注意

如果您使用 Laravel 附带的内置 LoginController,控制器使用的 trait 已经实现了“记住”用户的正确逻辑。

如果您正在“记住”用户,可以使用 viaRemember 方法来确定用户是否通过“记住我”cookie 进行认证:

php
if (Auth::viaRemember()) {
    //
}

其他认证方法 ​

认证用户实例 ​

如果您需要将现有用户实例登录到应用程序中,可以使用用户实例调用 login 方法。给定的对象必须是 Illuminate\Contracts\Auth\Authenticatable 契约 的实现。Laravel 附带的 App\User 模型已经实现了此接口:

php
Auth::login($user);

// 登录并“记住”给定用户...
Auth::login($user, true);

您可以指定要使用的守卫实例:

php
Auth::guard('admin')->login($user);

通过 ID 认证用户 ​

要通过用户的 ID 将用户登录到应用程序中,可以使用 loginUsingId 方法。此方法接受您希望认证的用户的主键:

php
Auth::loginUsingId(1);

// 登录并“记住”给定用户...
Auth::loginUsingId(1, true);

一次性认证用户 ​

您可以使用 once 方法将用户登录到应用程序中以进行单个请求。不会使用会话或 cookies,这意味着此方法在构建无状态 API 时可能会有所帮助:

php
if (Auth::once($credentials)) {
    //
}

HTTP 基本认证 ​

HTTP 基本认证 提供了一种快速认证应用程序用户的方法,而无需设置专用的“登录”页面。要开始,请将 auth.basic 中间件 附加到您的路由。auth.basic 中间件包含在 Laravel 框架中,因此您无需定义它:

php
Route::get('profile', function () {
    // 只有已认证的用户可以进入...
})->middleware('auth.basic');

一旦中间件附加到路由,您在浏览器中访问路由时将自动提示输入凭据。默认情况下,auth.basic 中间件将使用用户记录上的 email 列作为“用户名”。

关于 FastCGI 的注意事项 ​

如果您使用 PHP FastCGI,HTTP 基本认证可能无法开箱即用。应将以下行添加到您的 .htaccess 文件中:

php
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

无状态 HTTP 基本认证 ​

您还可以使用 HTTP 基本认证而不在会话中设置用户标识符 cookie,这对于 API 认证特别有用。为此,定义一个中间件 调用 onceBasic 方法。如果 onceBasic 方法未返回响应,则请求可以进一步传递到应用程序中:

php
<?php

namespace App\Http\Middleware;

use Illuminate\Support\Facades\Auth;

class AuthenticateOnceWithBasicAuth
{
    /**
     * 处理传入请求。
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, $next)
    {
        return Auth::onceBasic() ?: $next($request);
    }

}

接下来,注册路由中间件 并将其附加到路由:

php
Route::get('api/user', function () {
    // 只有已认证的用户可以进入...
})->middleware('auth.basic.once');

登出 ​

要手动将用户从应用程序中登出,您可以使用 Auth facade 上的 logout 方法。这将清除用户会话中的认证信息:

php
use Illuminate\Support\Facades\Auth;

Auth::logout();

使其他设备上的会话失效 ​

Laravel 还提供了一种机制,用于使用户在其他设备上活动的会话失效并“登出”,而不使当前设备上的会话失效。此功能通常在用户更改或更新其密码时使用,并且您希望在保持当前设备认证的同时使其他设备上的会话失效。

在开始之前,您应确保 Illuminate\Session\Middleware\AuthenticateSession 中间件在 app/Http/Kernel.php 类的 web 中间件组中存在并取消注释:

php
'web' => [
    // ...
    \Illuminate\Session\Middleware\AuthenticateSession::class,
    // ...
],

然后,您可以使用 Auth facade 上的 logoutOtherDevices 方法。此方法要求用户提供其当前密码,您的应用程序应通过输入表单接受:

php
use Illuminate\Support\Facades\Auth;

Auth::logoutOtherDevices($password);

当调用 logoutOtherDevices 方法时,用户的其他会话将完全失效,这意味着他们将从之前认证的所有守卫中“登出”。

注意

当将 AuthenticateSession 中间件与 login 路由的自定义路由名称结合使用时,您必须覆盖应用程序异常处理程序上的 unauthenticated 方法,以正确重定向用户到您的登录页面。

添加自定义守卫 ​

您可以使用 Auth facade 上的 extend 方法定义自己的认证守卫。您应在 服务提供者 中放置此 extend 调用。由于 Laravel 已经附带了一个 AuthServiceProvider,我们可以将代码放在该提供者中:

php
<?php

namespace App\Providers;

use App\Services\Auth\JwtGuard;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 注册任何应用程序认证/授权服务。
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Auth::extend('jwt', function ($app, $name, array $config) {
            // 返回 Illuminate\Contracts\Auth\Guard 的实例...

            return new JwtGuard(Auth::createUserProvider($config['provider']));
        });
    }
}

如上例所示,传递给 extend 方法的回调应返回 Illuminate\Contracts\Auth\Guard 的实现。此接口包含一些方法,您需要实现这些方法以定义自定义守卫。一旦定义了自定义守卫,您可以在 auth.php 配置文件的 guards 配置中使用此守卫:

php
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

闭包请求守卫 ​

实现基于 HTTP 请求的自定义认证系统的最简单方法是使用 Auth::viaRequest 方法。此方法允许您使用单个闭包快速定义认证过程。

要开始,请在 AuthServiceProvider 的 boot 方法中调用 Auth::viaRequest 方法。viaRequest 方法接受一个认证驱动程序名称作为其第一个参数。此名称可以是描述您的自定义守卫的任何字符串。传递给方法的第二个参数应为一个闭包,该闭包接收传入的 HTTP 请求并返回用户实例,或者如果认证失败,则返回 null:

php
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

/**
 * 注册任何应用程序认证/授权服务。
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Auth::viaRequest('custom-token', function ($request) {
        return User::where('token', $request->token)->first();
    });
}

一旦定义了自定义认证驱动程序,您可以在 auth.php 配置文件的 guards 配置中将其用作驱动程序:

php
'guards' => [
    'api' => [
        'driver' => 'custom-token',
    ],
],

添加自定义用户提供者 ​

如果您不使用传统的关系数据库来存储用户,您将需要使用自己的认证用户提供者扩展 Laravel。我们将使用 Auth facade 上的 provider 方法来定义自定义用户提供者:

php
<?php

namespace App\Providers;

use App\Extensions\RiakUserProvider;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 注册任何应用程序认证/授权服务。
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Auth::provider('riak', function ($app, array $config) {
            // 返回 Illuminate\Contracts\Auth\UserProvider 的实例...

            return new RiakUserProvider($app->make('riak.connection'));
        });
    }
}

在使用 provider 方法注册提供者后,您可以在 auth.php 配置文件中切换到新的用户提供者。首先,定义一个使用新驱动程序的 provider:

php
'providers' => [
    'users' => [
        'driver' => 'riak',
    ],
],

最后,您可以在 guards 配置中使用此提供者:

php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
],

用户提供者契约 ​

Illuminate\Contracts\Auth\UserProvider 实现仅负责从持久存储系统(如 MySQL、Riak 等)中获取 Illuminate\Contracts\Auth\Authenticatable 实现。这两个接口允许 Laravel 认证机制继续运行,而不管用户数据如何存储或使用什么类型的类来表示它。

让我们看看 Illuminate\Contracts\Auth\UserProvider 契约:

php
<?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);
}

retrieveById 函数通常接收一个表示用户的键,例如来自 MySQL 数据库的自动递增 ID。应由方法检索并返回与 ID 匹配的 Authenticatable 实现。

retrieveByToken 函数通过其唯一的 $identifier 和存储在 remember_token 字段中的“记住我” $token 检索用户。与前一个方法一样,应返回 Authenticatable 实现。

updateRememberToken 方法使用新 $token 更新 $user 字段 remember_token。在成功的“记住我”登录尝试或用户登出时分配一个新的令牌。

retrieveByCredentials 方法接收在尝试登录应用程序时传递给 Auth::attempt 方法的凭据数组。然后,该方法应“查询”底层持久存储以查找与这些凭据匹配的用户。通常,此方法将使用 $credentials['username'] 上的“where”条件运行查询。然后,该方法应返回 Authenticatable 的实现。此方法不应尝试进行任何密码验证或认证。

validateCredentials 方法应将给定的 $user 与 $credentials 进行比较以认证用户。例如,此方法可能应使用 Hash::check 将 $user->getAuthPassword() 的值与 $credentials['password'] 的值进行比较。此方法应返回 true 或 false,指示密码是否有效。

可认证契约 ​

现在我们已经探讨了 UserProvider 上的每个方法,让我们看看 Authenticatable 契约。请记住,提供者应从 retrieveById、retrieveByToken 和 retrieveByCredentials 方法返回此接口的实现:

php
<?php

namespace Illuminate\Contracts\Auth;

interface Authenticatable
{
    public function getAuthIdentifierName();
    public function getAuthIdentifier();
    public function getAuthPassword();
    public function getRememberToken();
    public function setRememberToken($value);
    public function getRememberTokenName();
}

此接口很简单。getAuthIdentifierName 方法应返回用户的“主键”字段的名称,getAuthIdentifier 方法应返回用户的“主键”。在 MySQL 后端中,这将是自动递增的主键。getAuthPassword 应返回用户的哈希密码。此接口允许认证系统与任何用户类一起工作,而不管您使用什么 ORM 或存储抽象层。默认情况下,Laravel 在 app 目录中包含一个实现此接口的 User 类,因此您可以参考此类以获取实现示例。

事件 ​

Laravel 在认证过程中会触发各种 事件。您可以在 EventServiceProvider 中附加监听器到这些事件:

php
/**
 * 应用程序的事件监听器映射。
 *
 * @var array
 */
protected $listen = [
    'Illuminate\Auth\Events\Registered' => [
        'App\Listeners\LogRegisteredUser',
    ],

    'Illuminate\Auth\Events\Attempting' => [
        'App\Listeners\LogAuthenticationAttempt',
    ],

    'Illuminate\Auth\Events\Authenticated' => [
        'App\Listeners\LogAuthenticated',
    ],

    'Illuminate\Auth\Events\Login' => [
        'App\Listeners\LogSuccessfulLogin',
    ],

    'Illuminate\Auth\Events\Failed' => [
        'App\Listeners\LogFailedLogin',
    ],

    'Illuminate\Auth\Events\Logout' => [
        'App\Listeners\LogSuccessfulLogout',
    ],

    'Illuminate\Auth\Events\Lockout' => [
        'App\Listeners\LogLockout',
    ],

    'Illuminate\Auth\Events\PasswordReset' => [
        'App\Listeners\LogPasswordReset',
    ],
];