After learning Laravel soon, there are many obstacles. We organize some understanding on Auth components, and rewrite some debugging process of encryption for MD5 in Auth component password authentication mode, and share it with other beginners of Laravel.
Requirement description
Because the project is an old one, some data need to be migrated directly to the new project for direct use, including some design of the database need to continue to use. So only the logical part of the code can be changed.
User table: uc_user
Encryption: md5
Password field: user_pass
Auth::attempt checkout and login
Auth::once checkout is not logged in for one-time authorization, similar to api interface scenarios
Auth::logout logout logged-out users
Auth::user gets user information that has been logged in
Auth::check to see if the user has logged in
Where do these functions come from? Laravel document, check laravel code and debug it.
The file is located at: / vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
Debugging is a painful thing. The following is the process instructions after debugging, which are shared with all the friends you need to use.
1. Configure database account password information
Modify the. env file in the root directory, as shown in the figure, only modify the database information, other can be defaulted.
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. Create user controller and write test login code.
artisan:
php artisan make:controller UserController
You can also create files manually: / app/Http/Controllers/UserController.php
The document reads as follows:
<?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'); //Verify the account password, and the postdata data key is the name of the data storage field. $postdata = ['user_name' => $username, 'user_pass'=>$password]; $ret = Auth::attempt($postdata); if($ret){ return 'success'; } return $username.$password; } }
3. Increase routing mapping
/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. Test access
url: http://www.example.com/login?username=ellermister&password=admin888
Obviously, the current Auth validation generates different SQL statements than we expected.
1. user_pass cannot be used as a plaintext query condition.
2. The table name queried is not our table name.
For this reason, we debug and modify.
The first problem, we can usually solve through the configuration file.
View: / 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, ], ], ];
From the above information, combined with routing configuration (/ app/Http/Kernel.php, / app/Http/routes.php), we can see that the default web engine we use is users.
In the user provider configuration section, you can see that the driver of the current configuration is eloquent (object mode).
The loaded model is: App User:: class. Now let's modify the model file.
/app/User.php
<?php namespace App; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { protected $table = 'user';#Set the table name here /** * 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', ]; }
Later, we resolved that filtering the user_pass field was not used as a query condition.
Through the query function: functions+attempt
The validation function is located at: / vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
In the figure, we can see that $credentials is our $postdata user information.
The key point is the line with red lines, retrieve the user's information through the $postdata we provide, and then check the password correctly.
dd($this->provider);exit;
By debugging the provider, the location of the object file is obtained.
Open the file in the same directory: / vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
Locating retrieveByCredentials Method
Change its original field name: password to user_pass.
Request debugging again: url: http://www.example.com/login?Username=ellermister&password=admin888
We find that the basic sql statements are normal and the tables become user s. In fact, our tables all have a unified prefix uc_, which is configured under the modification:
/config/database.php
Request debugging again: url: http://www.example.com/login?Username=ellermister&password=admin888
Another mistake: undefined password
We tracked it to this file and looked at it in 116 lines.
After testing, it is found that $credentials is the data we provide, and the default password field is user_pass instead of password, which results in this error. Change it to user_pass.
Request debugging again: url: http://www.example.com/login?Username=ellermister&password=admin888
This time we still haven't output our default success, debug the file again:
/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
Get it because of the password verification problem, the validation call is still in the file:
/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
Method: Validate Credentials
The test found two problems:
The $user - > getAuthPassword () method field is wrong and password ciphertext cannot be obtained.
This - > hasher - > check () authentication method is inconsistent with our existing data ciphertext encryption method and cannot be correctly verified.
Continue tracking to the file where the method is located:
/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php
Modify it to user_pass.
In addition, the external authentication password is modified as follows:
/** * 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()); }
Request debugging again: url: http://www.example.com/login?Username=ellermister&password=admin888
At this time, we finally logged in successfully.
The above logic code is only recommended for debugging, because just now we are directly modifying the framework source code, which may cause unexpected problems. It is not recommended to do so, so the actual application of the project see the following modifications.
5. Correct modification of routine items
Tidy up the core document that we have just revised.
/ vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php Modify the Validation Password Field and Validation Method
/ vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php modifies the fields of the database to retrieve records
Other files can be ignored. If there is debugging code, it can be deleted. Only the two core files (located in the: / vendor directory) are actually modified.
In general, all components of laravel can be extended and modified, and its functions can be rewritten and extended without modifying the source files.
We will then extend the Auth component and modify it to our requirements.
6. New User Provider
Copy / vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
To / app/Foundation/Auth/EllerEloquentUserProvider.php
And modify the file name and class name (note that the location and name of the file can be customized at this time, not limited to this)
<?php namespace App\Foundation\Auth;#Notice the namespace here use Illuminate\Support\Str; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Hashing\Hasher as HasherContract; use Illuminate\Contracts\Auth\Authenticatable as UserContract; #And the class name below class EllerEloquentUserProvider implements UserProvider { /** * The hasher implementation. * * @var \Illuminate\Contracts\Hashing\Hasher */ protected $hasher; /** * The Eloquent user model. * * @var string */ protected $model; /* Currently omitted, please copy the contents of the original file. */ /** * 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()); } /* Currently omitted, please copy the contents of the original file. */ }
Inject this 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; #New introduction of namespaces 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); //For interception injection, Eller-eloquent customization needs to correspond to the configuration file. Auth::provider('Eller-eloquent', function ($app, $config) { return new EllerEloquentUserProvider($this->app['hash'], $config['model']); }); } }
Modify configuration
/ config/auth.php (excerpts below)
'providers' => [ 'users' => [ 'driver' => 'Eller-eloquent',#To modify this, you need to correspond to the injection of UserProvider above. 'model' => App\User::class, ], // 'users' => [ // 'driver' => 'database', // 'table' => 'users', // ], ],
7. Rewriting getAuthPassword
The getAuthPassword method of Authenticatable.php is restored and rewritten in the User model.
/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. Restore all modified core files and pass the test.
url: http://www.example.com/login?username=ellermister&password=admin888
Finally, the test passed.
It should be noted that:
Test login is to simply test login, regardless of other, only test whether the login is successful, and then see other. Otherwise, there may be a tragic situation before me, all kinds of problems are intertwined, it is difficult to distinguish, and it is difficult to persevere in the end.
For example, the landing Session mechanism of laravel, the Csrf security mechanism of laravel and the cryptographic encryption method.