學習不久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
調試是一件很痛苦的事情,如下是調試完畢整理的過程說明,分享給各位須要用到的朋友。
修改根目錄下 .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
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; } }
/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');
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
此時咱們終於登陸成功。
以上邏輯代碼只建議做爲調試使用,由於剛纔咱們都是直接修改框架源代碼,可能會帶來沒法預期的問題,很是不建議這麼作,那麼實際項目中應用請參見後面改法。
整理一下,咱們剛纔修改過的核心文件。
/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php 修改驗證密碼字段以及驗證方式
/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php 修改數據庫取回記錄的字段
其餘文件能夠忽略,若有調試代碼,能夠刪除掉,實際修改的文件只有以上兩個核心文件(位於:/vendor目錄)。
laravel通常狀況下全部組件均可以進行擴展修改,能夠不修改源文件的狀況下對其功能進行重寫擴展等。
咱們接下來進行擴展Auth組件,修改成咱們的需求。
複製 /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', // ], ],
將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; } }
url:http://www.example.com/login?username=ellermister&password=admin888
終於測試經過。
須要注意的是:
測試登陸就要單純的測試登陸,無論其餘的,只測試是否能登錄成功,再去看其餘的。不然有可能出現我以前的慘狀,各類問題纏繞在一塊兒,很難分辨,到最後很難堅持下去。
好比:laravel的落地Session機制、laravel的Csrf安全機制、密碼加密方法。