Encyption / Расшифровка полей формы в CakePHP 3

Я надеялся, что кто-то сможет помочь с моей задачей. Он должен быть «относительно простым»: я хочу, чтобы некоторые поля формы были зашифрованы, когда они добавлены / отредактированы и дешифрованы, когда их ищут торт. Вот код, который работает для меня в v2.7.2:

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

приложение / модель / 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 с виртуальными полями, но я просто не могу собрать все это вместе.

Спасибо за любые указатели

Related of "Encyption / Расшифровка полей формы в CakePHP 3"

Существует несколько способов решить эту проблему (обратите внимание, что следующий код представляет собой непроверенный примерный код! Перед использованием любого из них вам необходимо понять новые основы).

Тип настраиваемой базы данных

Один из них – это настраиваемый тип базы данных, который будет шифровать при привязке значений к оператору базы данных и расшифровать, когда будут получены результаты. Это вариант, который я бы предпочел.

Вот простой пример, предполагая, что столбцы db могут содержать двоичные данные.

ЦСИ / База данных / Тип / 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 / конфигурации / bootstrap.php

Зарегистрируйте пользовательский тип.

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

SRC / Модель / Таблица / 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; } // ... } 

См. Cookbook> Доступ к базе данных и ORM> Основы базы данных> Добавление пользовательских типов

beforeSave и formformers

Менее сухой и более плотный подход, и в основном порт вашего кода 2.x, должен был использовать beforeSave вызов / событие beforeSave и форматировщик результатов. Например, форматер результата может быть присоединен к событию / beforeFind событию beforeFind .

В beforeSave просто установите / получите значения в / из экземпляра прошедшего объекта, вы можете использовать Entity::has() , Entity::get() и Entity::set() или даже использовать доступ к массиву, поскольку сущности реализуют ArrayAccess .

Форматирующий результат – это, в основном, крючок для поиска, и вы можете использовать его, чтобы легко перебирать результаты и изменять их.

Вот базовый пример, который не нуждается в дальнейшей экарланизации:

 // ... 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; }); } ); } // ... } 

Смотрите также

  • Cookbook> Доступ к базе данных и ORM> Основы баз данных> Добавление пользовательских типов
  • Cookbook> Доступ к базе данных и ORM> Query Builder> Добавление расчетных полей
  • API> \ Cake \ Datasource \ EntityTrait
  • Поваренная книга> Учебники и примеры> Учебник по закладке Часть 2> Сохранение строки тега
  • API> \ Cake \ ORM \ Table

Изменить: @npm был прав о том, что виртуальные свойства не работают. Теперь я злюсь на себя за плохой ответ. служит мне для того, чтобы не проверять его до того, как я разместил сообщение.

Чтобы все было правильно, я использовал версию, использующую поведение, чтобы расшифровать поля по мере их чтения и зашифровать их по мере их записи в базу данных.

Примечание. Этот код в настоящее время не содержит никаких настраиваемых поисковых устройств, поэтому он не будет поддерживать поиск по зашифрованному полю.

например.

 $this->Patient->findByPatientFirstname('bob'); // this will not work 

Поведение

/src/Model/Behavior/EncryptBehavior.php

 <?php /** * */ namespace Cake\ORM\Behavior; use ArrayObject; use Cake\Collection\Collection; use Cake\Datasource\EntityInterface; use Cake\Datasource\ResultSetInterface; use Cake\Event\Event; use Cake\ORM\Behavior; use Cake\ORM\Entity; use Cake\ORM\Query; use Cake\ORM\Table; use Cake\ORM\TableRegistry; use Cake\Utility\Inflector; use Cake\Utility\Security; use Cake\Log\Log; /** * Encrypt Behavior */ class EncryptBehavior extends Behavior { /** * Default config * * These are merged with user-provided configuration when the behavior is used. * * @var array */ protected $_defaultConfig = [ 'key' => 'YOUR_KEY_KERE', /* set them in the EntityTable, not here */ 'fields' => [] ]; /** * Before save listener. * Transparently manages setting the lft and rght fields if the parent field is * included in the parameters to be saved. * * @param \Cake\Event\Event $event The beforeSave event that was fired * @param \Cake\ORM\Entity $entity the entity that is going to be saved * @return void * @throws \RuntimeException if the parent to set for the node is invalid */ public function beforeSave(Event $event, Entity $entity) { $isNew = $entity->isNew(); $config = $this->config(); $values = $entity->extract($config['fields'], true); $fields = array_keys($values); $securityKey = $config['key']; foreach($fields as $field){ if( isset($values[$field]) && !empty($values[$field]) ){ $entity->set($field, Security::encrypt($values[$field], $securityKey)); } } } /** * Callback method that listens to the `beforeFind` event in the bound * table. It modifies the passed query * * @param \Cake\Event\Event $event The beforeFind event that was fired. * @param \Cake\ORM\Query $query Query * @param \ArrayObject $options The options for the query * @return void */ public function beforeFind(Event $event, Query $query, $options) { $query->formatResults(function ($results){ return $this->_rowMapper($results); }, $query::PREPEND); } /** * Modifies the results from a table find in order to merge the decrypted fields * into the results. * * @param \Cake\Datasource\ResultSetInterface $results Results to map. * @return \Cake\Collection\Collection */ protected function _rowMapper($results) { return $results->map(function ($row) { if ($row === null) { return $row; } $hydrated = !is_array($row); $fields = $this->_config['fields']; $key = $this->_config['key']; foreach ($fields as $field) { $row[$field] = Security::decrypt($row[$field], $key); } if ($hydrated) { $row->clean(); } return $row; }); } } 

Таблица

/src/Model/Table/PatientsTable.php

 <?php namespace App\Model\Table; use App\Model\Entity\Patient; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; use Cake\Core\Configure; /** * Patients Model * */ class PatientsTable extends Table { /** * Initialize method * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config) { parent::initialize($config); $this->table('patients'); $this->displayField('id'); $this->primaryKey('id'); // will encrypt these fields automatically $this->addBehavior('Encrypt',[ 'key' => Configure::read('Security.key'), 'fields' => [ 'patient_surname', 'patient_firstname' ] ]); } } 

Я чувствую твою боль. уровень ORM в cakephp 3 радикально отличается от cake2. Они разделили модель объекта и таблицу ORM на два разных класса, а afterFind был удален. Я бы посмотрел на использование виртуальных свойств. Я думаю, он может быть подходящим для вашего случая использования.

Пример ниже.

 <?php namespace App\Model\Entity; use Cake\ORM\Entity; use Cake\Utility\Security; use Cake\Core\Configure; class Patient extends Entity { protected function _setPatientSurname($str) { $this->set('patient_surname', Security::encrypt($str, Configure::read('Security.key')); } protected function _setPatientFirstname($str) { $this->set('patient_firstname', Security::encrypt($str, Configure::read('Security.key')); } protected function _getPatientSurname() { return Security::decrypt($this->patient_surname, Configure::read('Security.key')); } protected function _getPatientFirstname() { return Security::decrypt($this->patient_first_name, Configure::read('Security.key')); } }