Skip to content

认证

介绍

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

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

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

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

数据库注意事项

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

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

此外,您应该验证您的 users(或等效)表包含一个可为空的、长度为 100 个字符的字符串 remember_token 列。此列将用于存储由您的应用程序维护的“记住我”会话的令牌。这可以通过在迁移中使用 $table->rememberToken(); 来完成。

认证快速入门

Laravel 开箱即用地提供了两个认证控制器,它们位于 App\Http\Controllers\Auth 命名空间中。AuthController 处理新用户注册和认证,而 PasswordController 包含帮助现有用户重置其忘记密码的逻辑。每个控制器都使用一个 trait 来包含其必要的方法。对于许多应用程序,您不需要修改这些控制器。

路由

Laravel 提供了一种快速的方法,通过一个简单的命令为认证生成所有需要的路由和视图:

php
php artisan make:auth

此命令应在新应用程序上使用,并将安装注册和登录视图,以及所有认证端点的路由。还将生成一个 HomeController,用于处理登录后请求到应用程序的仪表板。不过,您可以根据应用程序的需要自定义或甚至删除此控制器。

视图

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

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

认证

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

路径自定义

当用户成功认证后,他们将被重定向到 / URI。您可以通过在 AuthController 上定义一个 redirectTo 属性来自定义认证后的重定向位置:

php
protected $redirectTo = '/home';

当用户未成功认证时,他们将自动被重定向回登录表单位置。

要自定义用户在退出应用程序后被重定向的位置,您可以在 AuthController 上定义一个 redirectAfterLogout 属性:

php
protected $redirectAfterLogout = '/login';

如果此属性不存在,用户将被重定向到 / URI。

守卫自定义

您还可以自定义用于认证用户的“守卫”。要开始,请在 AuthController 上定义一个 guard 属性。此属性的值应与 auth.php 配置文件中配置的守卫之一相对应:

php
protected $guard = 'admin';

验证 / 存储自定义

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

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

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

检索已认证的用户

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

php
$user = Auth::user();

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

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

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

确定当前用户是否已认证

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

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

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

保护路由

路由中间件 可用于仅允许已认证的用户访问给定路由。Laravel 附带 auth 中间件,该中间件在 app\Http\Middleware\Authenticate.php 中定义。您只需将中间件附加到路由定义即可:

php
// 使用路由闭包...

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

// 使用控制器...

Route::get('profile', [
    'middleware' => 'auth',
    'uses' => 'ProfileController@show'
]);

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

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

指定守卫

在将 auth 中间件附加到路由时,您还可以指定应使用哪个守卫来执行认证:

php
Route::get('profile', [
    'middleware' => 'auth:api',
    'uses' => 'ProfileController@show'
]);

指定的守卫应与 auth.php 配置文件的 guards 数组中的一个键相对应。

认证节流

如果您使用 Laravel 的内置 AuthController 类,Illuminate\Foundation\Auth\ThrottlesLogins trait 可用于限制对应用程序的登录尝试。默认情况下,如果用户在多次尝试后未能提供正确的凭据,他们将无法登录一分钟。节流是针对用户的用户名 / 电子邮件地址和他们的 IP 地址唯一的:

php
<?php

namespace App\Http\Controllers\Auth;

use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

class AuthController extends Controller
{
    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    // AuthController 类的其余部分...
}

手动认证用户

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

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

php
<?php

namespace App\Http\Controllers;

use Auth;

class AuthController extends Controller
{
    /**
     * 处理认证尝试。
     *
     * @return Response
     */
    public function authenticate()
    {
        if (Auth::attempt(['email' => $email, 'password' => $password])) {
            // 认证通过...
            return redirect()->intended('dashboard');
        }
    }
}

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

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

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

指定附加条件

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

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

在这些示例中,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)) {
    // 用户正在被记住...
}

如果您正在“记住”用户,您可以使用 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 方法在单个请求中将用户登录到应用程序中。不会使用会话或 cookie,这在构建无状态 API 时可能很有帮助。once 方法的签名与 attempt 方法相同:

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

HTTP基本认证

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

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

一旦中间件附加到路由,您在浏览器中访问路由时将自动提示输入凭据。默认情况下,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 Illuminate\Auth\Middleware;

use Auth;
use Closure;

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

}

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

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

重置密码

数据库注意事项

大多数 Web 应用程序提供了一种方法来让用户重置其忘记的密码。Laravel 提供了方便的方法来发送密码提醒和执行密码重置,而不是强迫您在每个应用程序上重新实现这一点。

要开始,请验证您的 App\User 模型实现了 Illuminate\Contracts\Auth\CanResetPassword 契约。当然,框架附带的 App\User 模型已经实现了此接口,并使用 Illuminate\Auth\Passwords\CanResetPassword trait 来包含实现接口所需的方法。

生成重置令牌表迁移

接下来,必须创建一个表来存储密码重置令牌。此表的迁移已包含在 Laravel 中,并位于 database/migrations 目录中。因此,您只需迁移:

php
php artisan migrate

路由

Laravel 包含一个 Auth\PasswordController,其中包含重置用户密码所需的逻辑。可以使用 make:auth Artisan 命令生成执行密码重置所需的所有路由:

php
php artisan make:auth

视图

同样,Laravel 在执行 make:auth 命令时将生成所有必要的密码重置视图。这些视图放置在 resources/views/auth/passwords 中。您可以根据应用程序的需要自由自定义它们。

重置密码后

一旦您定义了重置用户密码的路由和视图,您可以简单地在浏览器中访问 /password/reset 路由。框架附带的 PasswordController 已经包含了发送密码重置链接电子邮件以及更新数据库中密码的逻辑。

密码重置后,用户将自动登录到应用程序并重定向到 /home。您可以通过在 PasswordController 上定义一个 redirectTo 属性来自定义密码重置后的重定向位置:

php
protected $redirectTo = '/dashboard';
lightbulb

默认情况下,密码重置令牌在一小时后过期。您可以通过 config/auth.php 文件中的密码重置 expire 选项更改此设置。

自定义

认证守卫自定义

在您的 auth.php 配置文件中,您可以配置多个“守卫”,这些守卫可用于为多个用户表定义认证行为。您可以通过向控制器添加 $guard 属性来自定义包含的 PasswordController 以使用您选择的守卫:

php
/**
 * 应使用的认证守卫。
 *
 * @var string
 */
protected $guard = 'admins';

密码代理自定义

在您的 auth.php 配置文件中,您可以配置多个密码“代理”,这些代理可用于重置多个用户表上的密码。您可以通过向控制器添加 $broker 属性来自定义包含的 PasswordController 以使用您选择的代理:

php
/**
 * 应使用的密码代理。
 *
 * @var string
 */
protected $broker = 'admins';

添加自定义守卫

您可以使用 Auth facade 上的 extend 方法定义自己的认证守卫。您应该在 服务提供者 中放置此 provider 调用:

php
<?php

namespace App\Providers;

use Auth;
use App\Services\Auth\JwtGuard;
use Illuminate\Support\ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 执行服务的注册后引导。
     *
     * @return void
     */
    public function boot()
    {
        Auth::extend('jwt', function($app, $name, array $config) {
            // 返回 Illuminate\Contracts\Auth\Guard 的实例...

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

    /**
     * 在容器中注册绑定。
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

如上例所示,传递给 extend 方法的回调应返回 Illuminate\Contracts\Auth\Guard 的实现。此接口包含一些方法,您需要实现这些方法以定义自定义守卫。

一旦定义了自定义守卫,您可以在 guards 配置中使用该守卫:

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

添加自定义用户提供者

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

php
<?php

namespace App\Providers;

use Auth;
use App\Extensions\RiakUserProvider;
use Illuminate\Support\ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 执行服务的注册后引导。
     *
     * @return void
     */
    public function boot()
    {
        Auth::provider('riak', function($app, array $config) {
            // 返回 Illuminate\Contracts\Auth\UserProvider 的实例...
            return new RiakUserProvider($app['riak.connection']);
        });
    }

    /**
     * 在容器中注册绑定。
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

在您使用 provider 方法注册提供者后,您可以在 config/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 和“记住我” $token 检索用户,存储在字段 remember_token 中。与前一个方法一样,应返回 Authenticatable 实现。

updateRememberToken 方法使用新 $token 更新 $user 字段 remember_token。新令牌可以是成功“记住我”登录尝试时分配的新令牌,也可以是用户注销时的 null。

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

validateCredentials 方法应将给定的 $user$credentials 进行比较以认证用户。例如,此方法可能会将 $user->getAuthPassword() 字符串与 $credentials['password']Hash::make 进行比较。此方法应仅验证用户的凭据并返回布尔值。

可认证契约

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

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\Attempting' => [
        'App\Listeners\LogAuthenticationAttempt',
    ],

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

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

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