【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安全機制、密碼加密方法。

相關文章
相關標籤/搜索