【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

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

此时我们终于登录成功。

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

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

终于测试通过。

需要注意的是:

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏乐沙弥的世界

配置共享服务器模式

两者完成相同的任务,即处理所有指定的SQL操作。假定从客户端提交一个任意查询(DQL)到数据库服务器不论是专用模式还是共享

29430
来自专栏Hadoop实操

CDH集群升级Python3异常问题分析

在CDH集群中所有节点/opt/cloudera/anaconda3部署了Python3的安装包,如下描述:

30910
来自专栏我和未来有约会

Silverlight 3.0 中的 Local Connection

现在很多的需求中需要一个插件实例和另一个实例进行通讯。在同一个页面中调用Html、js等来通讯,而这个往往有一些限制,需要专门的去设置一些权限。在Silverl...

22470
来自专栏ytkah

wordpress无法安装这个包。: PCLZIP_ERR_MISSING_FILE (-4) : Missing archive file 'C:\WINDOWS\TEMP/wordpress-4.

朋友的wp博客好久没管理了,让ytkah帮忙打理一下,进到后台发现版本还是3.9的,那是比较早以前的版本了,早该升级了。 在升级wordpress时出现以下错误...

37580
来自专栏逸鹏说道

直传文件到Azure Storage的Blob服务中

题记:为了庆祝获得微信公众号赞赏功能,忙里抽闲分享一下最近工作的一点心得:如何直接从浏览器中上传文件到Azure Storage的Blob服务中。 为什么 如果...

39570
来自专栏Porschev[钟慰]的专栏

Windows Server 2008 R2 配置Exchange 2010邮件服务器并使用EWS发送邮件

配置环境 配置环境完全在此前一篇文章搭建好的环境下进行配置: http://www.cnblogs.com/zhongweiv/archive/201...

56480
来自专栏Netkiller

PHP高级编程之守护进程

PHP高级编程之守护进程 http://netkiller.github.io/journal/php.daemon.html 摘要 2014-09-01 发表...

41170
来自专栏互联网杂技

SpringBoot ( 十二 ) :SpringBoot 如何测试打包部署

有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发、调试...

10620
来自专栏移动开发的那些事儿

Android Sqlite并发问题

如上异常堆栈中的错误信息error code 5: database is locked,经过查找发现code为5代表sqlite中的SQLITE_BUSY异常...

19640
来自专栏飞雪无情的博客

Go语言经典库使用分析(五)| Negroni 中间件(一)

上一篇介绍的Gorilla Handlers中间件,严格来说不能称之为一个库或者框架,他只是一系列包装http.Handler的中间件函数,并且他们之间没有任何...

10130

扫码关注云+社区

领取腾讯云代金券