[Laravel 5] Auth component rewrite password authentication for MD5 encryption

Keywords: PHP Laravel Database SQL

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.

Posted by mattm1712 on Tue, 02 Jul 2019 13:24:43 -0700