【Laravel5】Auth组件重写密码认证方式为MD5加密

学习不久Laravel,碰壁很是多,整理一些 Auth组件上的理解,并重写Auth组件密码认证方式为MD5加密的一些调试过程,分享给其余初学Laravel的用户。php

需求说明

因为项目是一个老项目,须要将部分数据直接迁移到新项目中直接使用,包括数据库的一些设计须要继续沿用。因此能改动的只有代码逻辑部分。mysql

用户表:uc_userlaravel

加密方式 : md5web

密码字段:user_passsql

 

 

Auth::attempt 校验并登陆数据库

Auth::once 校验不登陆,用于一次性受权,相似与api接口的场景api

Auth::logout 注销登陆的用户安全

Auth::user 获取已经登陆的用户信息bash

Auth::check 检查用户是否已经登陆session

以上函数来源于哪里?laravel文档、查阅laravel代码打debug得知。

文件位于:/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php

调试是一件很痛苦的事情,如下是调试完毕整理的过程说明,分享给各位须要用到的朋友。

 

1.配置数据库帐户密码信息

修改根目录下 .env 文件便可,如图,只修改数据库信息便可,其余可默认。

APP_ENV=local
APP_DEBUG=true
APP_KEY=base64:IxkVvrRLqdJeU9h8vGu1W58OG3NVuDtkMWyC6nIT4qs=
APP_URL=http://www.example.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=ucenter_example
DB_USERNAME=root
DB_PASSWORD=root

CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

 

2.建立用户控制器,编写测试登陆代码。

artisan:

php artisan make:controller UserController

一样也能够手工进行建立文件:/app/Http/Controllers/UserController.php

文件内容以下:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use Auth;
class UserController extends Controller
{
    //
    public function login(){
    	$username = request()->get('username');
    	$password = request()->get('password');
		
		//验证帐号密码,postdata数据key为数据库存储字段名。
		$postdata = ['user_name' => $username, 'user_pass'=>$password];
		$ret = Auth::attempt($postdata);
		if($ret){
			return 'success';
		}
		return $username.$password;
    }
}

 

3.增长路由映射

/app/Http/routes.php

<?php

/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::get('/login', 'UserController@login');

 

4.测试进行访问

url:http://www.example.com/login?username=ellermister&password=admin888

很明显当前Auth验证生成的SQL语句和咱们预想的不太同样。

1. user_pass 不能做为明文查询条件。

2.所查询的表名不是咱们的表名。

为此咱们进行调试修改。

第一个问题,咱们通常经过配置文件就能够解决掉。

查看:/config/auth.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    */

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

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    */

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

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

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'email' => 'auth.emails.password',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

];

 

经过以上信息,结合路由配置(/app/Http/Kernel.php、/app/Http/routes.php),能够看到咱们默认使用的web引擎,其配置的provider是users。

在users provider配置段能够看到当前配置的驱动是 eloquent (对象方式)。

加载的模型是:App\User::class  此刻咱们修改这个模型文件。

/app/User.php

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
	protected $table = 'user';#在这里设置表名
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

 

以后,咱们解决过滤user_pass这个字段不被做为查询条件。

经过查询函数: function\s+attempt

获得验证函数位于:/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php

图中咱们能够看到 $credentials 就是咱们传出的 $postdata 用户信息。

重点是画红线的那句,经过咱们提供的$postdata来取回用户信息,以后再进行校验口令是否正确。

dd($this->provider);exit;

经过调试这个provider,获得对象文件位置。

打开同级目录下文件:/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php

定位到 retrieveByCredentials 方法

将其原字段名:password 修改成 user_pass。

再次请求调试:url:http://www.example.com/login?username=ellermister&password=admin888

咱们发现基本sql语句正常,表也变成了user,可实际咱们的表均有统一的前缀uc_,在此进行修改下配置:

/config/database.php

再次请求调试:url:http://www.example.com/login?username=ellermister&password=admin888

又是一个错误:未定义 password

咱们追踪到这个文件中,116行进行查看。

通过测试,发现$credentials是咱们提供数据,密码默认字段是user_pass而不是password,导致致使这个错误。改掉它为 user_pass。

再次请求调试:url:http://www.example.com/login?username=ellermister&password=admin888

这次依然没有输出咱们预设的success,再次调试文件:

/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php

获得是由于密码校验问题,验证调用依然是在文件:

/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php

方法:validateCredentials

测试发现两个问题:

$user->getAuthPassword()  方法字段取错,没法获取到密码密文;

$this->hasher->check()  验证方式和咱们已有的数据密文加密方式不一致,没法正确校验。

继续追踪到这个方法所在的文件:

/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php

将其进行修改成 user_pass。

另外修改外部验证密码方式为:

/**
     * 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['user_pass'];

		return md5($plain)===$user->getAuthPassword()?true:false;
        return $this->hasher->check($plain, $user->getAuthPassword());
    }

 

再次请求调试:url:http://www.example.com/login?username=ellermister&password=admin888

此时咱们终于登陆成功。

以上逻辑代码只建议做为调试使用,由于刚才咱们都是直接修改框架源代码,可能会带来没法预期的问题,很是不建议这么作,那么实际项目中应用请参见后面改法。

5.常规项目正确修改

整理一下,咱们刚才修改过的核心文件。

/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php 修改验证密码字段以及验证方式

/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php 修改数据库取回记录的字段

其余文件能够忽略,若有调试代码,能够删除掉,实际修改的文件只有以上两个核心文件(位于:/vendor目录)。

 

laravel通常状况下全部组件均可以进行扩展修改,能够不修改源文件的状况下对其功能进行重写扩展等。

咱们接下来进行扩展Auth组件,修改成咱们的需求。

 

6.新建 UserProvider

复制 /vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php

到 /app/Foundation/Auth/EllerEloquentUserProvider.php

并修改文件名以及类名(注意,此时文件的位置以及命名彻底能够自定义,不要限定于此)

<?php

namespace App\Foundation\Auth;#注意这里的命名空间

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

#以及下方的类名
class EllerEloquentUserProvider implements UserProvider
{
    /**
     * The hasher implementation.
     *
     * @var \Illuminate\Contracts\Hashing\Hasher
     */
    protected $hasher;

    /**
     * The Eloquent user model.
     *
     * @var string
     */
    protected $model;

    /* 当前省略 请复制原文件内容便可 */

    /**
     * Retrieve a user by the given credentials.
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        if (empty($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, 'user_pass')) {
                $query->where($key, $value);
            }
        }

        return $query->first();
    }

    /**
     * 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['user_pass'];
		
		return md5($plain)===$user->getAuthPassword()?true:false;
        return $this->hasher->check($plain, $user->getAuthPassword());
    }

    /* 当前省略 请复制原文件内容便可 */
}

 

注入这个UserProvider

/app/Providers/AuthServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

#新引入命名空间
use Auth;
use App\Foundation\Auth\EllerEloquentUserProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any application authentication / authorization services.
     *
     * @param  \Illuminate\Contracts\Auth\Access\Gate  $gate
     * @return void
     */
    public function boot(GateContract $gate)
    {
        $this->registerPolicies($gate);

        //进行拦截注入,Eller-eloquent 自定义须要与配置文件对应。
        Auth::provider('Eller-eloquent', function ($app, $config) {
            return new EllerEloquentUserProvider($this->app['hash'], $config['model']);
        });
    }
}

 

修改配置

/config/auth.php(如下节选)

'providers' => [
        'users' => [
            'driver' => 'Eller-eloquent',#修改此处,需与上文注入UserProvider对应。
            'model' => App\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

 

7.重写 getAuthPassword 方法

将Authenticatable.php的getAuthPassword 方法恢复,在User模型里进行重写。

/app/User.php

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
	protected $table = 'user';
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
	
    /**
     * Get the password for the user.
     *
     * @return string
     */
    public function getAuthPassword()
    {
        return $this->user_pass;
    }
}

 

8.恢复全部修改核心文件,测试经过。

url:http://www.example.com/login?username=ellermister&password=admin888

终于测试经过。

须要注意的是:

测试登陆就要单纯的测试登陆,无论其余的,只测试是否能登录成功,再去看其余的。不然有可能出现我以前的惨状,各类问题缠绕在一块儿,很难分辨,到最后很难坚持下去。

好比:laravel的落地Session机制、laravel的Csrf安全机制、密码加密方法。

相关文章
相关标签/搜索