一尘不染

CakePHP 3中表单域的加密/解密

php

我想在添加/编辑某些表单域时对其进行加密,并在通过cake查找它们时对其进行解密。这是v2.7.2中对我有用的代码:

core.php

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

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方法,但是我不能将它们全部放在一起。


阅读 265

收藏
2020-05-29

共1个答案

一尘不染

有多种方法可以解决此问题(请注意,以下代码是未经测试的示例代码!在使用任何此类代码之前,您应该首先了解新的基础知识)。

自定义数据库类型

一种是自定义数据库类型,它将在将值绑定到数据库语句时进行加密,并在获取结果时进行解密。那是我希望的选择。

这是一个简单的示例,假设db列可以容纳二进制数据。

src /数据库/类型/CryptedType.php

这应该是自言自语的,转换为数据库时进行加密,转换为PHP时进行解密。

<?php
namespace App\Database\Type;

use Cake\Database\Driver;
use Cake\Database\Type;
use Cake\Utility\Security;

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

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

src / config / bootstrap.php

注册自定义类型。

use Cake\Database\Type;
Type::map('crypted', 'App\Database\Type\CryptedType');

src / Model / Table / PatientsTable.php

最后,将可加密的列映射到注册的类型,就是这样,从现在开始,所有内容都会自动处理。

// ...

use Cake\Database\Schema\Table as Schema;

class PatientsTable extends Table
{
    // ...

    protected function _initializeSchema(Schema $table)
    {
        $table->columnType('patient_surname', 'crypted');
        $table->columnType('patient_first_name', 'crypted');
        return $table;
    }

    // ...
}

参见 **[食谱

数据库访问和ORM>数据库基础>添加自定义类型](http://book.cakephp.org/3.0/en/orm/database-
basics.html#adding-custom-types)**

beforeSave和结果格式化程序

较不干燥且紧密的耦合方法(基本上是2.x代码的一部分)将使用beforeSave回调/事件和结果格式化程序。结果格式化程序可以例如附加在beforeFind事件/回调中。

beforeSave刚刚设置/获取值/从通过实体实例,你可以利用Entity::has()Entity::get()并且Entity::set(),甚至使用数组访问,因为实体实施ArrayAccess

结果格式化程序基本上是一个after find挂钩,您可以使用它轻松地遍历结果并进行修改。

这是一个基本示例,不需要进一步解释:

// ...

use Cake\Event\Event;
use Cake\ORM\Query;

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::salt())
                );
            }
        }
        return true;
    }

    public function beforeFind(Event $event, Query $query, \ArrayObject $options, boolean $primary)
    {
        $query->formatResults(
            function ($results) {
                /* @var $results \Cake\Datasource\ResultSetInterface|\Cake\Collection\CollectionInterface */
                return $results->map(function ($row) {
                    /* @var $row array|\Cake\DataSource\EntityInterface */

                    foreach($this->encryptedFields as $fieldName) {
                        if(isset($row[$fieldName])) {
                            $row[$fieldName] = Security::decrypt($row[$fieldName], Security::salt());
                        }
                    }

                    return $row;
                });
            }
        );  
    }

    // ...
}

要对此进行一点解耦,您还可以将其移为一个行为,以便您可以轻松地在多个模型之间共享它。

2020-05-29