更改Laravel的密码加密方式

我们知道laravel在数据库存储时默认的加密算法是Bcrypt,可以使用提供的辅助函数bcrypt 或者使用Hash Facade 来完成密码的加密,使用Laravel的Auth Facade来完成认证,实际上,我们在很多项目因为各种原因并不使用Bcrypt加密存储用户密码,而很多项目还在使用md5或者自己造的乱七八糟的加密方式,这里举个例子使用base64(password + salt)的加密方式来尝试无痛的更换laravel的密码加密方式,从而能够使用laravel的Auth,当然,实际的项目中我相信没人用这样的加密方式。这个例子有助于理解laravel的ServiceProvider(提供给容器怎么获取实例的方法),Facade(给类起个别名并通过魔术方法调用该类中的方法), Contracts(一堆interface)和Container(一个数组中放了很多library的名字,不是类名哦,是自己想起什么起什么)直接上我的烂代码:

首先,我们仿照laravel的原来的Bcrypt的加密实现(BcryptHasher)类去实现laravel的Hasher Contract:
在app下新建一个目录 Password 并新建一个文件 PasswordHasher.php 挨个实现Hasher Contract定义的接口方法:

<?php

namespace App\Password;

use RuntimeException;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;

class PasswordHasher implements HasherContract
{
    /**
     * Default crypt salt factor.
     *
     * @var string
     */
    protected $salt = 'example_string';

    /**
     * Hash the given value.
     *
     * @param  string  $value
     * @param  array   $options
     * @return string
     *
     * @throws \RuntimeException
     */
    public function make($value, array $options = [])
    {
        $hash = base64_encode($value.$this->salt($options));
        if ($hash === false) {
            throw new RuntimeException('base64 hashing not supported.');
        }

        return $hash;
    }

    /**
     * Check the given plain value against a hash.
     *
     * @param  string  $value
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function check($value, $hashedValue, array $options = [])
    {
        if (strlen($hashedValue) === 0) {
            return false;
        }

        $hash = base64_encode($value.$this->salt($options));

        return (bool) ($hash === $hashedValue);
    }

    /**
     * Check if the given hash has been hashed using the given options.
     *
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function needsRehash($hashedValue, array $options = [])
    {
        return false;
    }

    /**
     * Set the default password work factor.
     *
     * @param  string  $salt
     * @return $this
     */
    public function setSalt($salt)
    {
        $this->salt = (string) $salt;

        return $this;
    }

    /**
     * Extract the cost value from the options array.
     *
     * @param  array  $options
     * @return string
     */
    protected function salt(array $options = [])
    {
        return isset($options['salt']) ? $options['salt'] : $this->salt;
    }
}

所以我们到现在已经掌握了laravel里面的一个装逼的概念名词:Contracts ,他就是定义好的一些接口,我们去实现它就行。

接着我们再在该目录下创建一个文件 PasswordServiceProvider.php:

<?php

namespace App\Password;

use Illuminate\Support\ServiceProvider;

class PasswordServiceProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = true;

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {

        $this->app->singleton('password', function () {
            return new PasswordHasher;
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return ['password'];
    }

}

在PasswordServiceProvider中我们告诉容器($app即$this->app)password这个东西就是new PasswordHasher,这样当你从容器中拿password,容器就知道到哪去拿,该怎么拿,接着我们把这个ServiceProvider加载到框架中去:
在config/app.php中providers数组中加入

`App\Password\PasswordServiceProvider::class,

现在你可以test一下,打印下$app['password']的值了或者$app->make('password')的值,他们是一样的。所以我们就掌握了ServiceProvider这个装逼的概念。

下面我们在Password目录下,新建一个目录叫Facade并新建一个文件 Password.php:

<?php

namespace App\Password\Facade;
use Illuminate\Support\Facades\Facade;
/**
 * @see \App\Password\PasswordHasher
 */
class Password extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'password';
    }
}

然后我们在config/app.php中的aliases 数组中添加
`'Pwd' => App\Password\Facade\Password::class,//Password被框架的auth.password占用了哦

这样我们在项目中就可以直接使用Pwd::make($value)来调用PasswordHasher的make方法了。
所以我们又掌握了Facade这个装逼的概念的就是个类起个别名,并且告诉框架使用这个别名真正应该调用容器中的哪个类,这个例子我们通过ServiceProvider告诉Container password 是new PasswordHasher ,Facade 给password 起了个别名叫Pwd,在getFacadeAccessor方法中告诉了他在容器中的名字叫password。

最后,在完善下,我们在Password目录下建立一个文件叫help.php:

<?php

if(!function_exists('password')){
    /**
     * generate password
     * @param $value
     * @param array $options
     * @return mixed
     */
    function password($value, $options = [])
    {
        return app('password')->make($value, $options);
    }
}

然后在composer.json中require下面将这个files 加入,运行composer dumpautoload 重新生成加载规则。
这样我们就可以随时使用password($value) 替代Pwd::make($vale) with($app->make('password'))->make($value)等七八种看起来稀奇古怪的写法。

嗯,啥都写好了,怎么在Auth Facade中用呢?

找到配置文件:config/auth.php
我们只需要更改Auth的provider即可,laravel自带的database provider 和eloquent provider 这两个类,都是同时接受一个hasher 和一个database/model,所以我们只需要更改自带的provider的传入参数将现在的PasswordHasher实例传入而不是原来的BcryptHasher,我们来扩展下Auth的provider,修改app/Providers/AuthServiceProvider.php:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Support\Facades\Auth;
use Illuminate\Auth\DatabaseUserProvider;
class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        //自定义eloquent provider
        Auth::provider('custom_eloquent', function () {
            $config = $this->app['config']['auth.providers.custom_eloquent'];
            return new EloquentUserProvider($this->app['password'], $config['model']);
        });

        //自定义database provider
        Auth::provider('custom_database', function () {
            $config = $this->app['config']['auth.providers.custom_database'];
            $connection = $this->app['db']->connection();
            return new DatabaseUserProvider($connection, $this->app['password'], $config['table']);
        });

    }
}

这样我们就自己创建了自定义的database和eloquent Auth Provider,改一下配置文件的defualt数组,现在就可以直接使用Auth中的所有方法,并且密码的加密方式已经是自己写的了哦,不要被那些概念唬住了.. 代码最起码要写的不能跟框架格格不入吧,大不了硬编码了,太晚了,睡觉了,马虎结尾了哦...

添加新评论