Laravel 实践 – 自定义认证

Laravle认证原理:http://blog.ifeeline.com/2726.html

从一个旧系统迁移数据,密码取了哈希,为了兼容认证,比如修改认证方式。

Laravel中可以新增Guard,也可以新增Provider,所以一般都是通过新增的方式实现更换原有的Guard和Provider。

vi config/auth.php

<?php

return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            //'provider' => 'users',
            // 这里的compatible_users对应providers中的compatible_users,名字随意
            'provider' => 'compatible_users'
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        // 自定义Provider,driver对应的值必须和定义时一直
        'compatible_users' => [
            'driver' => 'compatible',
            'model' => App\User::class,
        ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

];
[/php
这里设置了一个自定义的Provider,他对应的driver叫compatible,通过\Auth::provider添加这个“compatible”

vi app/Providers/AuthServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Extensions\CompatibleUserProvider;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    public function boot()
    {
        $this->registerPolicies();

        \Auth::provider('compatible', function($app, array $config) {
            return new CompatibleUserProvider($app, $config['model']);
        });
    }
}

这里需要定义App\Extensions\CompatibleUserProvider,它集成了原来的类:

vi app/Extensions/CompatibleUserProvider.php

<?php

namespace App\Extensions;

use Illuminate\Support\Str;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Auth\EloquentUserProvider;

class CompatibleUserProvider extends EloquentUserProvider
{
    /**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(UserContract $user, array $credentials)
    {
        $plain = $credentials['password'];
        $password = $user->getAuthPassword();
        if (strlen($password) <= 32) {
            return hash_equals(md5($plain), $password);
        }
        return $this->hasher->check($plain, $user->getAuthPassword());
    }
}

覆盖了父类方法:validateCredentials(),通过这个方法注入自己的验证逻辑。

另外,为了validateCredentials()方法需要得到用户实例,如果没有用户实例,那就没有验证这个说法了,用户实例是通过retrieveByCredentials获取的:

//Illuminate\Auth\EloquentUserProvider

/**
     * Retrieve a user by the given credentials.
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        if (empty($credentials) ||
           (count($credentials) === 1 &&
            array_key_exists('password', $credentials))) {
            return;
        }

        // First we will add each credential element to the query as a where clause.
        // Then we can execute the query and, if we found a user, return it in a
        // Eloquent User "model" that will be utilized by the Guard instances.
        $query = $this->createModel()->newQuery();

        foreach ($credentials as $key => $value) {
            if (! Str::contains($key, 'password')) {
                $query->where($key, $value);
            }
        }

        return $query->first();
    }

可以通过修改这个方法来修改取回用户实例的逻辑,不过仔细看就知道它不过是根据$credentials来查询用户表而已,所以只需要定义$credentials即可:

#Illuminate\Foundation\Auth是一个trait,它被App\Http\Controllers\Auth\LoginController使用
#用来从请求中抽取需要认证的信息
    /**
     * Get the needed authorization credentials from the request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function credentials(Request $request)
    {
        return $request->only($this->username(), 'password');
    }

可以添加需要验证的字段和对应的值。这样的条件会被用来查找用户实例。如果找不到用户实例,最终会返回验证不通过。