学习不久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安全机制、密码加密方法。