认证

介绍

laravel实施认证非常的简单。事实上,所有事情都可以在配置文件中完成。授权的配置文件为config/auth.php,他包含几个选项会影响授权服务的行为。

在他的核心,Laravel的授权的实施是由guards和providers组成的。Guards定义了用户怎样认证每一个请求的。比如说,session guard和token guard。

Providers定义了用户如何从持久存储的库(如数据库)中检索用户的。比如通过Eloquent和查询构建器检索。然后你可以根据自己应用的需要添加自己的providers。

不要担心,如果这些听起来让你很困惑。对于大多是应用我们都不需要更爱默认的配置。

数据库注意事项

默认,laravel会包含一个App\User模型在app目录。这个模型可以用于默认的Eloquent认证驱动。如果你的应用程序不使用Eloquent,你也可以使用数据库认证驱动。

当我们再构建App\User模型的数据库的时候,需要确保password字段的长度大于60个字符。

同时你需要确保你的users表拥有一个可为null,长度为100的remember_token字段。这个字段将被用来存储一个记住我的token,你可以在数据迁移中使用这个代码$table->rememberToken();创建他。

快速入门

laravel包含两个控制器,他们都在本地的App\Http\Controllers\Auth命名空间下。AuthController处理用户的注册和认证,PasswordController帮助用户找回密码。每一个控制器都使用trait来包含他们必要的方法。对于许多应用你都不需要修改这些控制器。

路由

laravel提供了一个简单的命令来快速的构建你需要用于认证的路由和视图:

php artisan make:auth

这个命令用于新建的应用程序,他将创建一个注册和登录的视图,和用于认证的路由。同时也会生成一个HomeController,他将用来显示你登录后的信息,然而,你可以在你的应用中自定义他甚至于删除他。

视图

如前所述,这个php artisan make:auth命令将会创建所有的你需要的认证视图,并将它们创建到resources/views/auth目录。

make:auth命令也会创建一个resources/views/layouts的目录,并且包含一个基础的布局(layout)模板,这些视图都可以使用Bootstrap,然而你也可以自定它们。

认证

现在你的路由和授权控制器都设置好了,你需要去创建一个并认证一个新的用户!你可以简单的通过浏览器打开你的应用。这个控制器已经包含了认证已存在的用户和创建一个新的用户到数据库的逻辑。

定制路劲

当用户成功认证以后,他们需要跳转到某个路劲,你可以设置AuthControllerredirectTo属性来自定义你需要跳转的路径:

protected $redirectTo = '/home';


如果用户认证失败,用户将自动定位到登录的表单页。

自定义Guard

你也许需要自定义一个guard用户认证用户。首先,定义一个guard属性到你的AuthController,这个属性值将与auth.php配置文件中的guards配置对应。

protected $guard = 'admin';

自定义验证和存储

假如你需要修改注册时的表单字段,或者修改数据库字段时,你也许需要修改AuthController控制器。这个类负责验证和创建一个新的用户到你的应用。

AuthControllervalidator包含了创建一个新用户到你的应用程序的规则。你可以根据自己的需要修改他。

AuthController的create方法使用Eloquent ORM创建一个新的用户记录到数据库。你也可以修改这个方法将用户映射到你需要的数据库。

检索认证的用户

你可以通过Auth facade(别名系统)访问已认证的用户:

$user = Auth::user();

另外,一旦用户已认证,你可以通过Illuminate\Http\Request的实例访问认证了的用户。记住,类型提示将自动讲他注入到你的控制器的方法:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProfileController extends Controller
{
    /**
     * Update the user's profile.
     *
     * @param  Request  $request
     * @return Response
     */
    public function updateProfile(Request $request)
    {
        if ($request->user()) {
            // $request->user() returns an instance of the authenticated user...
        }
    }
}

确认用户是否已经认证

确认用户是否已经登录到你的应用,你可以使用Auth facade的check方法,如果他返回true,那么用户就已经认证。

if (Auth::check()) {
    // The user is logged in...
}

然而,你也可以通过中间件验证用户是否已经认证,当用户访问你的某些路由或控制器的时候。

保护路由

路由中间件可以用来控制认证用户访问给定的路由。laravel有一个auth中间件,定义在app\Http\Middleware\Authenticate.php,你所需要做的是将中间件加入到路由的定义之中。

// Using A Route Closure...

Route::get('profile', ['middleware' => 'auth', function() {
    // Only authenticated users may enter...
}]);

// Using A Controller...

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

当然,你也可以在控制器中定义,你需要在控制器的构造函数中调用middleware方法。

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

指定Guard

当我们将auth中间件加入到路由之中,我们也许需要指定使用哪个guard来认证

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

指定的guard需要与auth.php配置的guards数组的键对应上。

认证节流

如果你使用的是laravel内置的AuthController控制器,那么Illuminate\Foundation\Auth\ThrottlesLogins将在你登录的时候被使用。默认,当用户在一分钟内多次认证失败,那么用户将不允许登录。这个节流只是正对用户的用户名或邮箱和用户的登录ip:

<?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;

    // Rest of AuthController class...
}


手动认证

当然,你可以不使用laravel内置的认证控制器。如果你删除了他们,你也可以直接使用laravel的认证服务管理你的用户认证。

你需要通过Auth别名访问laravel的认证服务,那么你首先需要在你的顶部use Auth,然后使用attempt方法:

<?php

namespace App\Http\Controllers;

use Auth;

class AuthController extends Controller
{
    /**
     * Handle an authentication attempt.
     *
     * @return Response
     */
    public function authenticate()
    {
        if (Auth::attempt(['email' => $email, 'password' => $password])) {
            // Authentication passed...
            return redirect()->intended('dashboard');
        }
    }
}

attempt方法的第一个参数是一个键值对的数组。这些值将被用来在数据库表中找到用户,所以,上面的例子,用户将会通过email字段检索到。如果找到用户,我们将对加密的密码进行匹配,如果匹配成功,那么一个认证用户的session将被启动。

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

转向器(redirector)的intended方法会将用户转向到,用户尝试访问,但被认证拦截了的地址。如果这个地址不可用,则会跳转到指定的地址。

指定添加的条件

如果你希望,你也可以在认证的时候增加更多的查询条件。比如,我们可以验证用户是否被激活。

if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
    // The user is active, not suspended, and exists.
}

注意,在这个例子当中email不是必须的选项,这里只是用他来举例。你可以使用你数据库中任何与账号对应的字段。

访问指定的Guard实例

你可以用Auth的guard方法指定你想要的guard的实例。这可以让你完全分离你的认证模块或用户表。

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

注销

你可以在应用中使用Auth的logout方法注销用户。他将清除掉用户认证的session。

记住我

如果你希望在你的应用中提供记住我这样的功能,你需要传递一个bool值到attempt方法的第二个参数,他将一直保持用户的认证状态,直到你手动的注销。当然,你的用户表需要包含remember_token字段,他将用来存储记住我的口令。

if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
    // The user is being remembered...
}

如果你使用的记住我功能,你可以使用Auth的viaRemember方法确认用户是否是使用记住我的cookie来认证的。

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

其他认证方法

认证一个用户的实例

如果你需要用一个已经存在的用户的实例登录你的应用,你可以调用login方法,但是用户的实例必须实现Illuminate\Contracts\Auth\Authenticatable接口,当然,laravel自带的App\User模型已经实现这个接口。

Auth::login($user);

通过ID认证用户

你可以使用loginUsingId方法,让用户通过ID登录到你的系统,你只需要将你希望认证的用户的主键传递过去就可以了:

Auth::loginUsingId(1);

认证用户通过一次

你可以是用once方法让用户通过一个请求的认证,他不会使用session和cookie。当你需要构建一个无状态的API的时候他也许可以帮助到你。once方法和attempt方法一样:

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

HTTP 基础认证

HTTP基础认证提供了一个快捷的方式认证你应用程序中的用户,而不需要创建专门的登录页面。首先,我们需要将auth.basic中间件加入到路由。laravel已经自带了auth.basic中间件,你不需要再定义它了:

Route::get('profile', ['middleware' => 'auth.basic', function() {
    // Only authenticated users may enter...
}]);

当中间件加入路由,浏览器会自动提示你输入凭据。默认,auth.basic中间件使用email字段当做用户名。

FastCGI标注

如果你使用PHP的FastCGI,HTTP基础验证不能正确的工作。你需要将一下的代码加入到.htaccess文件。

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

无状态的HTTP基础认证

你也许需要用户通过HTTP基础认证但不保存session,比如通过API认证的时候。如果要这么做,我们需要定义一个调用onceBasic的中间件,如果响应没有被onceBasic方法返回,那么请求将进一步的通过你的应用程序。

<?php

namespace Illuminate\Auth\Middleware;

use Auth;
use Closure;

class AuthenticateOnceWithBasicAuth
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return Auth::onceBasic() ?: $next($request);
    }

}

然后,注册中间件到你的路由。

Route::get('api/user', ['middleware' => 'auth.basic.once', function() {
    // Only authenticated users may enter...
}]);

重置密码

数据库注意事项

大部分应用都提供了一种方式让用户重置他们忘记了的密码。而不是强制用户重新注册,laravel提供了方便的方法发送密码提醒和进行密码重置。

首先,你的App\User模型需要实现Illuminate\Contracts\Auth\CanResetPassword接口。当然,框架自带的App\User模型已经实现了这个接口,并且使用Illuminate\Auth\Passwords\CanResetPassword trait实现了该接口的所有方法。

创建密码重置令牌表

我们需要一个存储密码重置令牌的表,框架中已经包含了这个表的数据迁移,在database/migrations目录。你只需要执行数据迁移:

php artisan migrate

路由

laravel有一个Auth\PasswordController控制器,他包含了重置密码的必要逻辑。生成重置密码的所有路由,可以使用make:auth命令:

php artisan make:auth

视图

make:auth执行后,所有的重置密码需要的视图都会生成。这些视图在resources/views/auth/passwords目录,你可以根据应用程序的需要修改他们。

重置密码以后

当你重置密码的路由和视图都已经定义了,你可以在浏览其中访问这个路由/password/reset。PasswordController控制器已经包含了发送重置密码的链接到邮箱和修改用户密码的逻辑。

当密码重置后,用户将自动登录到应用程序,并跳转至/home。通过设置PasswordControllerredirectTo属性,你可以自定义重置密码后跳转的地址:

protected $redirectTo = '/dashboard';

注意:默认,重置密码令牌的过期时间为1个小时。你可以在config/auth.php配置文件中修改重置密码的expire选项改变他。

定制

定制Guard

在你的auth.php配置文件中,你可以配置多个guard,他们用来定义多个用户表的认证行为。你可以设置PasswordController$guard属性来使用你指定的gurad。

/**
 * The authentication guard that should be used.
 *
 * @var string
 */
protected $guard = 'admins';

自定义Password Broker

在你的auth.php配置文件中,你可以配置多个broker,他们用来重置多个用户表的密码。你可以设置PasswordController$broker属性来使用你指定的broker。

/**
 * The password broker that should be used.
 *
 * @var string
 */
protected $broker = 'admins';

添加自定义的Guard

你可以通过Auth的extend方法定义你自己的Guard。你需要在ServieProvider中定义他们。

<?php

namespace App\Providers;

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

class AuthServiceProvider extends ServiceProvider
{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        Auth::extend('jwt', function($app, $name, array $config) {
            // Return an instance of Illuminate\Contracts\Auth\Guard...

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

    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

如上所述,extend接收一个回调函数,并且该回调函数需要返回一个实现了该Illuminate\Contracts\Auth\Guard接口的Guard。

当你自定义的guard已经定义后,你需要去auth.php配置文件中的guards节点配置他:

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

添加自定义的用户提供者

如果你不想使用传统的关系数据库去存储你的用户,你需要用你自己的用户提供者扩展laravel。我们可以使用Auth的provider方法添加自定义的用户提供者。你需要在ServiceProvider中调用他:

<?php

namespace App\Providers;

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

class AuthServiceProvider extends ServiceProvider
{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        Auth::provider('riak', function($app, array $config) {
            // Return an instance of Illuminate\Contracts\Auth\UserProvider...
            return new RiakUserProvider($app['riak.connection']);
        });
    }

    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

当你通过provider方法注册了提供者以后,你可以在config/auth.php配置文件中开启这个用户提供者。首先,定义一个provider使用新的驱动:

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

然后你需要在guards配置中使用这个提供者:

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

用户提供者接口

Illuminate\Contracts\Auth\UserProvider只负责从可持久化的库中获取一个Illuminate\Contracts\Auth\Authenticatable,比如说MySQL,Riak,etc。通过这两个接口,不管用户使用什么数据存储或者使用什么类型的类代表他(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);

}

retrieveById方法通常用来接收代表用户的键,例如,MySql数据自动增长的ID。这个方法需要接收一个Authenticatable接口匹配的ID和返回一个实现该接口的对象。

retrieveByToken函数接收一个用户的$token,这个$token是唯一的,用户记住我功能的,存储在remember_token字段。和上面的方法一样他会放回一个Authenticatable接口的实现。

updateRememberToken方法会用一个新的token更新用户的remember_token字段。当用户使用记住我功能成功登陆后会更新一个新的令牌,如果退出则更新为null。

当用户尝试登陆我们的应用的时候,Auth::attempt会传递一个数组形式的凭据给retrieveByCredentials方法。这个方法然后查询底层的持久化存储库(比如MySql数据库)来匹配用户的凭证。通常,这个方法会运行一个条件为$credentials['username']的查询。这个方法返回一个UserInterface借口的实现。这个方法不会尝试任何的密码验证和认证

这个validateCredentials方法会比较$user和$credentials来认证用户。例如,这个方法可以会将$user->getAuthPassword()的返回值和$credentials['password']通过Hash::make加密后的值进行比较。这个方法只是验证用户的凭据并返回一个bool值。

Authenticatable接口

现在让我们来探索UserProvider的每一个方法,我们来看看Authenticatable接口。记住,retrieveById方法retrieveByCredentials方法需要返回这个接口的实现。

<?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会包含一个实现该接口的User类在app目录,所以你可以把这个类作为一个实现该接口的案例。

事件

laravel在认证过程中触发了各种事件,你可以在EventServiceProvider中增加这些事件的监听。

/**
 * The event listener mappings for the application.
 *
 * @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',
    ],
];

 

posted @ 2016-01-21 11:42  wh-王东  阅读(820)  评论(0编辑  收藏  举报