Skip to content

认证

介绍

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

数据库注意事项

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

在为App\User模型构建数据库架构时,请确保密码列至少为60个字符长。

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

认证快速入门

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

路由

默认情况下,没有包含指向认证控制器的路由。您可以手动将它们添加到您的app/Http/routes.php文件中:

php
// 认证路由...
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');

// 注册路由...
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');

视图

虽然认证控制器已包含在框架中,但您需要提供这些控制器可以渲染的视图。视图应放置在resources/views/auth目录中。您可以随意自定义这些视图。登录视图应放置在resources/views/auth/login.blade.php,注册视图应放置在resources/views/auth/register.blade.php

示例认证表单

php
<!-- resources/views/auth/login.blade.php -->

<form method="POST" action="/auth/login">
    {!! csrf_field() !!}

    <div>
        邮箱
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        密码
        <input type="password" name="password" id="password">
    </div>

    <div>
        <input type="checkbox" name="remember"> 记住我
    </div>

    <div>
        <button type="submit">登录</button>
    </div>
</form>

示例注册表单

php
<!-- resources/views/auth/register.blade.php -->

<form method="POST" action="/auth/register">
    {!! csrf_field() !!}

    <div>
        姓名
        <input type="text" name="name" value="{{ old('name') }}">
    </div>

    <div>
        邮箱
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        密码
        <input type="password" name="password">
    </div>

    <div>
        确认密码
        <input type="password" name="password_confirmation">
    </div>

    <div>
        <button type="submit">注册</button>
    </div>
</form>

认证

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

当用户成功认证时,他们将被重定向到/home URI,您需要注册一个路由来处理。您可以通过在AuthController上定义一个redirectPath属性来自定义认证后的重定向位置:

php
protected $redirectPath = '/dashboard';

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

php
protected $loginPath = '/login';

loginPath不会更改用户在尝试访问受保护路由时被弹回的位置。这由App\Http\Middleware\Authenticate中间件的handle方法控制。

自定义

要修改新用户注册时所需的表单字段,或自定义如何将新用户记录插入到数据库中,您可以修改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;
use Illuminate\Routing\Controller;

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');
}

认证节流

如果您使用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;
use Illuminate\Routing\Controller;

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])) {
    // 用户是活跃的,没有被暂停,并且存在。
}

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

php
Auth::logout();

NOTE

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

记住用户

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

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

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

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

其他认证方法

认证用户实例

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

php
Auth::login($user);

通过ID认证用户

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

php
Auth::loginUsingId(1);

一次性认证用户

您可以使用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,其中包含重置用户密码所需的逻辑。但是,您需要定义路由以将请求指向此控制器:

php
// 密码重置链接请求路由...
Route::get('password/email', 'Auth\PasswordController@getEmail');
Route::post('password/email', 'Auth\PasswordController@postEmail');

// 密码重置路由...
Route::get('password/reset/{token}', 'Auth\PasswordController@getReset');
Route::post('password/reset', 'Auth\PasswordController@postReset');

视图

除了为PasswordController定义路由外,您还需要提供此控制器可以返回的视图。别担心,我们将提供示例视图以帮助您入门。当然,您可以随意设置表单样式。

示例密码重置链接请求表单

您需要为密码重置请求表单提供一个HTML视图。此视图应放置在resources/views/auth/password.blade.php。此表单提供了一个字段供用户输入电子邮件地址,以便请求密码重置链接:

php
<!-- resources/views/auth/password.blade.php -->

<form method="POST" action="/password/email">
    {!! csrf_field() !!}

    @if (count($errors) > 0)
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    @endif

    <div>
        邮箱
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        <button type="submit">
            发送密码重置链接
        </button>
    </div>
</form>

当用户提交请求以重置其密码时,他们将收到一封电子邮件,其中包含指向PasswordControllergetReset方法(通常路由为/password/reset)的链接。您需要在resources/views/emails/password.blade.php中为此电子邮件创建一个视图。该视图将接收包含密码重置令牌的$token变量,以匹配用户与密码重置请求。以下是一个示例电子邮件视图以帮助您入门:

php
<!-- resources/views/emails/password.blade.php -->

点击此处重置您的密码:{{ url('http://example.com/password/reset/'.$token) }}

示例密码重置表单

当用户点击电子邮件中的链接以重置其密码时,他们将看到一个密码重置表单。此视图应放置在resources/views/auth/reset.blade.php

以下是一个示例密码重置表单以帮助您入门:

php
<!-- resources/views/auth/reset.blade.php -->

<form method="POST" action="/password/reset">
    {!! csrf_field() !!}
    <input type="hidden" name="token" value="{{ $token }}">

    @if (count($errors) > 0)
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    @endif

    <div>
        邮箱
        <input type="email" name="email" value="{{ old('email') }}">
    </div>

    <div>
        密码
        <input type="password" name="password">
    </div>

    <div>
        确认密码
        <input type="password" name="password_confirmation">
    </div>

    <div>
        <button type="submit">
            重置密码
        </button>
    </div>
</form>

重置密码后

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

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

php
protected $redirectTo = '/dashboard';

NOTE

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

社交认证

除了典型的基于表单的认证,Laravel还提供了一种简单、方便的方法来使用Laravel Socialite进行OAuth提供商认证。Socialite目前支持与Facebook、Twitter、LinkedIn、Google、GitHub和Bitbucket的认证。

要开始使用Socialite,请将其添加到您的composer.json文件中作为依赖项:

php
composer require laravel/socialite

配置

安装Socialite库后,在您的config/app.php配置文件中注册Laravel\Socialite\SocialiteServiceProvider

php
'providers' => [
    // 其他服务提供者...

    Laravel\Socialite\SocialiteServiceProvider::class,
],

此外,将Socialite facade添加到app配置文件中的aliases数组中:

php
'Socialite' => Laravel\Socialite\Facades\Socialite::class,

您还需要为您的应用程序使用的OAuth服务添加凭据。这些凭据应放置在您的config/services.php配置文件中,并应使用facebooktwitterlinkedingooglegithubbitbucket作为键,具体取决于您的应用程序所需的提供商。例如:

php
'github' => [
    'client_id' => 'your-github-app-id',
    'client_secret' => 'your-github-app-secret',
    'redirect' => 'http://your-callback-url',
],

基本用法

接下来,您可以认证用户了!您需要两个路由:一个用于将用户重定向到OAuth提供商,另一个用于在认证后接收来自提供商的回调。我们将使用Socialite facade访问Socialite:

php
<?php

namespace App\Http\Controllers;

use Socialite;
use Illuminate\Routing\Controller;

class AuthController extends Controller
{
    /**
     * 将用户重定向到GitHub认证页面。
     *
     * @return Response
     */
    public function redirectToProvider()
    {
        return Socialite::driver('github')->redirect();
    }

    /**
     * 从GitHub获取用户信息。
     *
     * @return Response
     */
    public function handleProviderCallback()
    {
        $user = Socialite::driver('github')->user();

        // $user->token;
    }
}

redirect方法负责将用户发送到OAuth提供商,而user方法将读取传入的请求并从提供商处检索用户的信息。在重定向用户之前,您还可以使用scope方法在请求上设置“范围”。此方法将覆盖所有现有范围:

php
return Socialite::driver('github')
            ->scopes(['scope1', 'scope2'])->redirect();

当然,您需要为控制器方法定义路由:

php
Route::get('auth/github', 'Auth\AuthController@redirectToProvider');
Route::get('auth/github/callback', 'Auth\AuthController@handleProviderCallback');

许多OAuth提供商支持在重定向请求中包含可选参数。要在请求中包含任何可选参数,请使用关联数组调用with方法:

php
return Socialite::driver('google')
            ->with(['hd' => 'example.com'])->redirect();

检索用户详细信息

一旦您有了用户实例,您可以获取有关用户的更多详细信息:

php
$user = Socialite::driver('github')->user();

// OAuth Two Providers
$token = $user->token;

// OAuth One Providers
$token = $user->token;
$tokenSecret = $user->tokenSecret;

// All Providers
$user->getId();
$user->getNickname();
$user->getName();
$user->getEmail();
$user->getAvatar();

添加自定义认证驱动

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

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::extend('riak', function($app) {
            // 返回一个Illuminate\Contracts\Auth\UserProvider实例...
            return new RiakUserProvider($app['riak.connection']);
        });
    }

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

在您使用extend方法注册驱动后,您可以在config/auth.php配置文件中切换到新驱动。

用户提供者合同

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()字符串与Hash::make$credentials['password']进行比较。此方法应仅验证用户的凭据并返回布尔值。

认证合同

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

php
<?php

namespace Illuminate\Contracts\Auth;

interface Authenticatable {

    public function getAuthIdentifier();
    public function getAuthPassword();
    public function getRememberToken();
    public function setRememberToken($value);
    public function getRememberTokenName();

}

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

事件

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

php
/**
 * 为应用程序注册任何其他事件。
 *
 * @param  \Illuminate\Contracts\Events\Dispatcher  $events
 * @return void
 */
public function boot(DispatcherContract $events)
{
    parent::boot($events);

    // 在每次认证尝试时触发...
    $events->listen('auth.attempt', function ($credentials, $remember, $login) {
        //
    });

    // 在成功登录时触发...
    $events->listen('auth.login', function ($user, $remember) {
        //
    });

    // 在注销时触发...
    $events->listen('auth.logout', function ($user) {
        //
    });
}