【Laravel5】Auth组件重写密码认证方式为MD5加密

学习不久Laravel,碰壁非常多,整理一些 Auth组件上的理解,并重写Auth组件密码认证方式为MD5加密的一些调试过程,分享给其他初学Laravel的用户。

需求说明

由于项目是一个老项目,需要将部分数据直接迁移到新项目中直接使用,包括数据库的一些设计需要继续沿用。所以能改动的只有代码逻辑部分。

用户表:uc_user

加密方式 : md5

密码字段:user_pass

 

 

Auth::attempt 校验并登录

Auth::once 校验不登录,用于一次性授权,类似与api接口的场景

Auth::logout 注销登录的用户

Auth::user 获取已经登录的用户信息

Auth::check 检查用户是否已经登录

以上函数来源于哪里?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

021630_uZRW_2366984.png

很明显当前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

023536_0WBb_2366984.png

图中我们可以看到 $credentials 就是我们传出的 $postdata 用户信息。

重点是画红线的那句,通过我们提供的$postdata来取回用户信息,之后再进行校验口令是否正确。

dd($this->provider);exit;

通过调试这个provider,得到对象文件位置。

023929_F9F9_2366984.png

023950_SV3F_2366984.png

打开同级目录下文件:/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php

定位到 retrieveByCredentials 方法

024140_RoCO_2366984.png

将其原字段名:password 修改为 user_pass。

再次请求调试:url:http://www.example.com/login?username=ellermister&password=admin888

024433_af2X_2366984.png

我们发现基本sql语句正常,表也变成了user,可实际我们的表均有统一的前缀uc_,在此进行修改下配置:

/config/database.php

024603_XzIn_2366984.png

再次请求调试:url:http://www.example.com/login?username=ellermister&password=admin888

024703_IK5j_2366984.png

又是一个错误:未定义 password

我们追踪到这个文件中,116行进行查看。

024831_n3SJ_2366984.png

经过测试,发现$credentials是我们提供数据,密码默认字段是user_pass而不是password,致使导致这个错误。改掉它为 user_pass。

再次请求调试:url:http://www.example.com/login?username=ellermister&password=admin888

025057_AUw8_2366984.png

此次依然没有输出我们预设的success,再次调试文件:

/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php

得到是因为密码校验问题,验证调用依然是在文件:

/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php

方法:validateCredentials

025255_Fjsd_2366984.png

测试发现两个问题:

$user->getAuthPassword()  方法字段取错,无法获取到密码密文;

$this->hasher->check()  验证方式和我们已有的数据密文加密方式不一致,无法正确校验。

继续追踪到这个方法所在的文件:

/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php

030529_9ykN_2366984.png

将其进行修改为 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

030557_CUQS_2366984.png

此时我们终于登录成功。

以上逻辑代码只建议作为调试使用,因为刚才我们都是直接修改框架源代码,可能会带来无法预期的问题,非常不建议这么做,那么实际项目中应用请参见后面改法。

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

033750_it6K_2366984.png

终于测试通过。

需要注意的是:

测试登录就要单纯的测试登录,不管其他的,只测试是否能登陆成功,再去看其他的。否则有可能出现我之前的惨状,各种问题缠绕在一起,很难分辨,到最后很难坚持下去。

比如:laravel的落地Session机制、laravel的Csrf安全机制、密码加密方法。

转载于:https://my.oschina.net/u/2366984/blog/895431

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值