CakePHP 3 中表单字段的加密/解密

2021-12-21 00:00:00 php cakephp-3.0 cakephp

我想在添加/编辑时加密一些表单字段,并在蛋糕查找它们时对其进行解密.这是在 v2.7.2 中对我有用的代码:

I want to have some form-fields encrypted when they are added/edited and decrypted when they are looked up by cake. Here is the code that works for me in v2.7.2:

core.php

Configure::write('Security.key','secretkey');

app/model/patient.php.

app/model/patient.php.

public $encryptedFields = array('patient_surname', 'patient_first_name');

public function beforeSave($options = array()) {
    foreach($this->encryptedFields as $fieldName){
        if(!empty($this->data[$this->alias][$fieldName])){
            $this->data[$this->alias][$fieldName] = Security::encrypt(
                $this->data[$this->alias][$fieldName],
                Configure::read('Security.key')
            );
        }
    }
    return true;
}

public function afterFind($results, $primary = false) {

    foreach ($results as $key => $val) {
        foreach($this->encryptedFields as $fieldName) {
            if (@is_array($results[$key][$this->alias])) {
                $results[$key][$this->alias][$fieldName] = Security::decrypt(
                    $results[$key][$this->alias][$fieldName],
                    Configure::read('Security.key')
                );
            }
        }
    }
    return $results;
}

据我所知,我必须将 $this->data[] 替换为模型生成的实体,并将 afterFind 方法替换为虚拟字段,但我无法将它们放在一起.

As I understand it I have to replace $this->data[] with the generated entities for the model and the afterFind method with virtual fields, but I just can't put it all together.

推荐答案

解决这个问题的方法不止一种(请注意以下代码是未经测试的示例代码!在使用任何新的基础知识之前,您应该先掌握新的基础知识)这个).

There's more than one way to solve this (please note that the following code is untested example code! You should get a grasp on the new basics first before using any of this).

一种是自定义数据库类型,它会在将值绑定到数据库语句时进行加密,并在获取结果时进行解密.这是我更喜欢的选项.

One would be a custom database type, which would encrypt when binding the values to the database statement, and decrypt when results are being fetched. That's the option that I would prefer.

这是一个简单的例子,假设 db 列可以保存二进制数据.

Here's simple example, assuming the db columns can hold binary data.

src/Database/Type/CryptedType.php

这应该是不言自明的,转换到数据库时加密,转换到 PHP 时解密.

This should be rather self explantory, encrypt when casting to database, decrypt when casting to PHP.

<?php
namespace AppDatabaseType;

use CakeDatabaseDriver;
use CakeDatabaseType;
use CakeUtilitySecurity;

class CryptedType extends Type
{
    public function toDatabase($value, Driver $driver)
    {
        return Security::encrypt($value, Security::getSalt());
    }

    public function toPHP($value, Driver $driver)
    {
        if ($value === null) {
            return null;
        }
        return Security::decrypt($value, Security::getSalt());
    }
}

src/config/bootstrap.php

注册自定义类型.

use CakeDatabaseType;
Type::map('crypted', 'AppDatabaseTypeCryptedType');

src/Model/Table/PatientsTable.php

最后将可加密列映射到注册类型,就是这样,从现在开始,一切都会自动处理.

Finally map the cryptable columns to the registered type, and that's it, from now on everything's being handled automatically.

// ...

use CakeDatabaseSchemaTable as Schema;

class PatientsTable extends Table
{
    // ...
    
    protected function _initializeSchema(Schema $table)
    {
        $table->setColumnType('patient_surname', 'crypted');
        $table->setColumnType('patient_first_name', 'crypted');
        return $table;
    }

    // ...
}

参见食谱>数据库访问ORM >数据库基础添加自定义类型

See Cookbook > Database Access & ORM > Database Basics > Adding Custom Types

一种不那么枯燥和紧密耦合的方法,基本上是您 2.x 代码的一个端口,将使用 beforeSave 回调/事件和结果格式化程序.例如,结果格式化程序可以附加在 beforeFind 事件/回调中.

A less dry and tighter coupled approach, and basically a port of your 2.x code, would be to use the beforeSave callback/event, and a result formatter. The result formatter could for example be attached in the beforeFind event/callback.

beforeSave 中只需设置/获取传入实体实例的值,您可以使用 Entity::has(), Entity::get()Entity::set(),甚至使用数组访问,因为实体实现了 ArrayAccess.

In beforeSave just set/get the values to/from the passed entity instance, you can utilize Entity::has(), Entity::get() and Entity::set(), or even use array access since entities implement ArrayAccess.

结果格式化程序基本上是一个 after find 钩子,您可以使用它轻松地迭代结果并修改它们.

The result formatter is basically an after find hook, and you can use it to easily iterate over results, and modify them.

这是一个基本的例子,不需要太多解释:

Here's a basic example, which shouldn't need much further explanation:

// ...

use CakeEventEvent;
use CakeORMQuery;

class PatientsTable extends Table
{
    // ...
    
    public $encryptedFields = [
        'patient_surname',
        'patient_first_name'
    ];
    
    public function beforeSave(Event $event, Entity $entity, ArrayObject $options)
    {
        foreach($this->encryptedFields as $fieldName) {
            if($entity->has($fieldName)) {
                $entity->set(
                    $fieldName,
                    Security::encrypt($entity->get($fieldName), Security::getSalt())
                );
            }
        }
        return true;
    }
    
    public function beforeFind(Event $event, Query $query, ArrayObject $options, boolean $primary)
    {
        $query->formatResults(
            function ($results) {
                /* @var $results CakeDatasourceResultSetInterface|CakeCollectionCollectionInterface */
                return $results->map(function ($row) {
                    /* @var $row array|CakeDataSourceEntityInterface */
                    
                    foreach($this->encryptedFields as $fieldName) {
                        if(isset($row[$fieldName])) {
                            $row[$fieldName] = Security::decrypt($row[$fieldName], Security::getSalt());
                        }
                    }
                    
                    return $row;
                });
            }
        );  
    }

    // ...
}

为了稍微解耦,您还可以将其移动到行为中,以便您可以轻松地在多个模型之间共享它.

To decouple this a little, you could also move this into a behavior so that you can easily share it across multiple models.

另见

  • 食谱>数据库访问ORM >数据库基础添加自定义类型
  • 食谱>数据库访问ORM >查询生成器添加计算字段
  • 食谱>教程 &例子 >书签教程第 2 部分 >保留标签字符串
  • 食谱>数据库访问ORM >行为
  • API >CakeDatasourceEntityTrait
  • API >CakeORMTable

相关文章