Laravel重写或者覆盖vender下面的方法

前几天在做一个需求,想要使用对称加密算法来加密一个即将被保存在数据库中的值,而从 Eloquent 模型访问该属性时自动解密其值。中间发现encrypt加密出的数据每次都会变化,希望能改为每次得到的加密字符串结果一致,但是加解密文件在vender下面,这就需要重写或覆盖vender下面的文件方法

 

结果:

新增 fixedencrypt() 函数, 你也可以覆盖原来的encrypt(),  因为我是希望保留一份原来的加密方式

 

实现:

           1. 加密方式使用laravel自带的加密函数 encrypt 和 decrypt,文档链接: 官方文档

           2.  使用修改器,来实现保存数据库时自动加密,取出时自动解密。文档: 修改器-官方文档

           3. 重写加密函数,文档:扩展包开发, 服务容器服务提供者

因为我使用的是lumen框架,稍有些出入,但实现思路是一样的

 

自动加解密数据库字段: 

 

  1. config/app.php文件 配置 APP_KEY 如下, 我本地需要在.env文件中配置APP_KEY

执行 php artisan key:generate 命令来生成 APP_KEY, 如果用的是 lumen 框架,框架内部简化了 artisan 命令, 执行命令时会报错,可用str_random(32);

  1. 数据库文件,修改set 和get方法,以下代码希望password字段保存时自动加密,取出自动加密
<?php

namespace BaisonBundle\Entities;

use Doctrine\ORM\Mapping as ORM;

/**
 * Test 测试表
 *
 * @ORM\Table(name="test", options={"comment"="测试表"})
 * @ORM\Entity(repositoryClass="BaisonBundle\Repositories\TestRepository")
 */
class Test
{
    /**
     * @var integer
     *
     * @ORM\Id
     * @ORM\Column(name="test_id", type="bigint", options={"comment":"test id"})
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $test_id;

    /**
     * @var string
     *
     * @ORM\Column(name="password", type="string", options={"comment":"password"})
     */
    private $password;


    /**
     * Get testId.
     *
     * @return int
     */
    public function getTestId()
    {
        return $this->test_id;
    }

    /**
     * Set password.
     *
     * @param string $password
     *
     * @return Test
     */
    public function setPassword($password)
    {
        $this->password = encrypt($password); //保存数据库时会自动加密

        return $this;
    }

    /**
     * Get password.
     *
     * @return string
     */
    public function getPassword()
    {
        return decrypt($this->password);//取出时自动解密
    }
}

    3.  新增一条数据

app('registry')->getManager('default')->getRepository(Test::class)->create(['password'=>'123456']);

    4. 数据库最终保存结果

 

重写或者覆盖vender,扩展fixedencryp函数:

 

  1. 创建service文件,src/EspierBundle/Services/EncrypterService.php 目录位置自定义
<?php
namespace EspierBundle\Services;


use Illuminate\Encryption\Encrypter;

/**
 * Class EncrypterService 继承vender下要扩展或重写的类
 *
 */
class EncrypterService extends Encrypter
{
    public function __construct($key, $cipher = 'AES-128-CBC')
    {
        parent::__construct($key, $cipher);
    }

    //要重写的方法及内容
    public function encrypt($value, $serialize = true)
    {
        // $iv = random_bytes(openssl_cipher_iv_length($this->cipher));
        $iv = '5322ff2432432422';

        // First we will encrypt the value using OpenSSL. After this is encrypted we
        // will proceed to calculating a MAC for the encrypted value so that this
        // value can be verified later as not having been changed by the users.
        $value = \openssl_encrypt(
            $serialize ? serialize($value) : $value,
            $this->cipher, $this->key, 0, $iv
        );

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

        // Once we get the encrypted value we'll go ahead and base64_encode the input
        // vector and create the MAC for the encrypted value so we can then verify
        // its authenticity. Then, we'll JSON the data into the "payload" array.
        $mac = $this->hash($iv = base64_encode($iv), $value);

        $json = json_encode(compact('iv', 'value', 'mac'));

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new EncryptException('Could not encrypt the data.');
        }

        return base64_encode($json);
    }
}

     2. 新增Provider文件,src/EspierBundle/Providers/FixedEncryptionServiceProvider.php目录位置自定义

<?php
namespace EspierBundle\Providers;


use EspierBundle\Services\EncrypterService;

use RuntimeException;
use Illuminate\Support\Str;
use Illuminate\Support\ServiceProvider;

/**
 * Class FixedEncryptionServiceProvider 继承ServiceProvider;
 * 
 * 所有服务提供器都会继承 Illuminate\Support\ServiceProvider 类。
 * 大多数服务提供器都包含 register 和 boot 方法。
 * 在 register 方法中,你只需要绑定类到 服务容器中。
 * 而不需要尝试在 register 方法中注册任何事件监听器、路由或任何其他功能。
 */
class FixedEncryptionServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('fixedencrypt', function ($app) {
            $config = $app->make('config')->get('app');

            // If the key starts with "base64:", we will need to decode the key before handing
            // it off to the encrypter. Keys may be base-64 encoded for presentation and we
            // want to make sure to convert them back to the raw bytes before encrypting.
            if (Str::startsWith($key = $this->key($config), 'base64:')) {
                $key = base64_decode(substr($key, 7));
            }

            return new EncrypterService($key, $config['cipher']);
        });
    }
    /**
     * Extract the encryption key from the given configuration.
     *
     * @param  array  $config
     * @return string
     */
    protected function key(array $config)
    {
        return tap($config['key'], function ($key) {
            if (empty($key)) {
                throw new RuntimeException(
                    'No application encryption key has been specified.'
                );
            }
        });
    }
}

     3. bootstrap/app.php 添加以下内容

$app->loadComponent('app', 'EspierBundle\Providers\FixedEncryptionServiceProvider', 'fixedencrypt');
$app->register(EspierBundle\Providers\FixedEncryptionServiceProvider::class);

到此可使用 app('fixedencrypt')->encrypt($value) 调用,如果想直接使用fixedencrypt() 

     4. 修改app/helpers.php 文件,新增以下内容;参考文档:如何在 Laravel 中创建自己的 PHP 辅助函数

    if (! function_exists('fixedencrypt')) {
        /**
         * Encrypt the given value.
         *
         * @param  string  $value
         * @return string
         */
        function fixedencrypt($value)
        {
            return app('fixedencrypt')->encrypt($value);
        }
    }

     可直接使用fixedencrypt()

扩展需求:为每台服务器设置独立的key值,而且又要保证数据能正确解密

  1. config/app.php 可自定义多个key值

        2.  src/EspierBundle/Providers/FixedEncryptionServiceProvider.php文件内容

<?php
namespace EspierBundle\Providers;


use EspierBundle\Services\EncrypterService;

use RuntimeException;
use Illuminate\Support\Str;
use Illuminate\Support\ServiceProvider;

/**
 * Class FixedEncryptionServiceProvider
 *
 */
class FixedEncryptionServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('fixedencrypt', function ($app) {
            return new EncrypterService();
        });
    }

}

     3.  src/EspierBundle/Services/EncrypterService.php 文件

<?php

namespace EspierBundle\Services;


use Illuminate\Encryption\Encrypter;

/**
 * Class EncrypterService
 *
 */
class EncrypterService extends Encrypter
{
    // 定义初始化
    public function __construct()
    {
        // If the key starts with "base64:", we will need to decode the key before handing
        // it off to the encrypter. Keys may be base-64 encoded for presentation and we
        // want to make sure to convert them back to the raw bytes before encrypting.
        // 如果没有传key的值,可以使用默认的key值
        $this->default();
    }
    /*
     * 设置默认的属性
     * 本类是一个单例模式的类,如果前期设置了配置,后期又想使用默认配置,你需要调用default方法才行
     */
    public function default(){
        $this->key = config('app.key');
        $this->cipher = config('app.cipher');
        return $this;
    }

    // 设置key的值
    public function setKey($key){
        $this->key = config('app.'.$key);
        return $this;
    }

    public function encrypt($value, $serialize = true)
    {
        // $iv = random_bytes(openssl_cipher_iv_length($this->cipher));
        $iv = 'X3xMx3dnoCtSprda';

        // First we will encrypt the value using OpenSSL. After this is encrypted we
        // will proceed to calculating a MAC for the encrypted value so that this
        // value can be verified later as not having been changed by the users.
        $value = \openssl_encrypt(
            $serialize ? serialize($value) : $value,
            $this->cipher, $this->key, 0, $iv
        );

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

        // Once we get the encrypted value we'll go ahead and base64_encode the input
        // vector and create the MAC for the encrypted value so we can then verify
        // its authenticity. Then, we'll JSON the data into the "payload" array.
        $mac = $this->hash($iv = base64_encode($iv), $value);

        $json = json_encode(compact('iv', 'value', 'mac'));

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new EncryptException('Could not encrypt the data.');
        }

        return base64_encode($json);
    }
}

    4. app/helpers.php

if (! function_exists('fixedencrypt')) {
        /**
         * Encrypt the given value.
         *
         * @param  string  $value
         * @return string
         */
        function fixedencrypt($value, $keyid='key')
        {
            return app('fixedencrypt')->setKey($keyid)->encrypt($value);
        }
    }

    if (! function_exists('fixeddecrypt')) {
        /**
         * Encrypt the given value.
         *
         * @param  string  $value
         * @return string
         */
        function fixeddecrypt($value, $keyid='key')
        {
            return app('fixedencrypt')->setKey($keyid)->decrypt($value);
        }
    }

     5. 调用

# 加密
fixedencrypt('123456', 'key1');//eyJpdiI6IldETjRUWGd6Wkc1dlEzUlRjSEprWVE9PSIsInZhbHVlIjoiaU9ycVwvNDJQd2tWeDZrV0NXNzhQZ3c9PSIsIm1hYyI6IjRmODY5NGRjZGE2NWZmNmI0MmM3OTQ4YmYwYWUyNTkwNDk1NmYyOWUzZWIyYjJkZjYxMTA4MTRjMDcxNDRiY2EifQ


# 解密
$str = "eyJpdiI6IldETjRUWGd6Wkc1dlEzUlRjSEprWVE9PSIsInZhbHVlIjoiaU9ycVwvNDJQd2tWeDZrV0NXNzhQZ3c9PSIsIm1hYyI6IjRmODY5NGRjZGE2NWZmNmI0MmM3OTQ4YmYwYWUyNTkwNDk1NmYyOWUzZWIyYjJkZjYxMTA4MTRjMDcxNDRiY2EifQ";
fixeddecrypt($str,'key1'); // 123456

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值