Laravel 框架加密解密如何实现 key 值多变的需求
需求分析
我们有一台总的数据处理服务器,又有很多台数据源服务器。为了让他们相互通信,我们在每个服务器上搭建了相同的laravel网站框架。分服务器之会和总服务器进行数据交互。
同时,为了数据安全,总服务器和分服务器之间的数据传输使用laravel默认的encrypt
加密和decrypt
解密。
起初,为了数据能被正确解密,每一台服务器的key值要保持一致。但是随之,问题也就来了。如果后期我们的一台分服务器数据泄露了的话,那么就意味着,别人掌握了我们的所有的数据,有很大的安全隐患。所以,我们需要为每台服务器设置独立的key值,而且又要保证数据能正确解密。
虽然非对称加密是个很好的解决方案,但是要自己额外安装依赖包,额外设置
原理分析
我通过对laravel的源码分析发现,encrypt()
其实是在app启动的时候,使用默认的key配置注册了一次类。
public function register()
{
$this->app->singleton('encrypter', 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 Encrypter($key, $config['cipher']);
});
}
以上代码摘自\vendor\laravel\framework\src\Illuminate\Encryption\EncryptionServiceProvider.php
既然是这样,我们就可以对该Encrypter
类进行多次实例化来达成我们的目的。
解决方法
我习惯在控制器
Controllers
目录多生成一个Service
目录,来放置控制类中经常使用的类和方法。这次我们使用如下命令新建了EncryptService
类。php artisan make:controller Service/EncryptService
使用单例模式来构建。
// 单例函数的实例化 static private $instance; static public function getInstance(){ if(!self::$instance){ self::$instance = new self(); } return self::$instance; } // 定义初始化 private 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(); }
getEncrypter
对配置进行判断和处理,并new
一个新的Encrypter
类,如下:/* * 加密之前的验证 */ private function getEncrypter(){ if (Str::startsWith($this->key, 'base64:')) { $this->key = base64_decode(substr($this->key, 7)); } $this->key = (string) $this->key; if (!static::supported($this->key, $this->cipher)) { throw new RuntimeException('The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.'); } return new Encrypter($this->key, $this->cipher); }
以
encrypt
为例,使用$this->getEncrypter()
方法来帮我们做加密中转。/* * 定义公开的加密函数 */ public function encrypt($value, $serialize = true){ // 处理之前进行一步验证 return $this->getEncrypter()->encrypt($value, $serialize); }
使用
setKey()
来设置每次的加密的值,使用default()
来恢复使用默认的值/* * 设置默认的属性 * 本类是一个单例模式的类,如果前期设置了配置,后期又想使用默认配置,你需要调用default方法才行 */ public function default(){ $this->key = config('app.key'); $this->cipher = config('app.cipher'); return $this; } // 设置key的值 public function setKey($key){ $this->key = $key; return $this; }
调用的时候,我们只需要对我们自己的
EncryptService
来操作就行了。使用setKey()
来设置新的key的值。如下use App\Http\Controllers\Controller; use App\Http\Controllers\Service\EncryptService;
class HomeController extends Controller { public function index(Content $content) { $encrypt = EncryptService::getInstance()->setKey('base64:uHNY0XQRxDLtQzl8ZWEMUDIbFGteGZA0o9BMP+L5sa8=')->encrypt('123'); dd($encrypt); } }
附录
以下是类的完整源码,有不足的地方,还请多多指教。
<?php
namespace App\Http\Controllers\Service;
use RuntimeException;
use Illuminate\Encryption\Encrypter;
use Illuminate\Support\Str;
class EncryptService
{
// 定义加密参数
private $key;
// 定义加密方式
private $cipher;
// 单例函数的实例化
static private $instance;
static public function getInstance(){
if(!self::$instance){
self::$instance = new self();
}
return self::$instance;
}
// 定义初始化
private 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 = $key;
return $this;
}
// 设置cipher的值
public function setCipher($cipher){
$this->cipher = $cipher;
return $this;
}
/*
* 定义公开的加密函数
*/
public function encrypt($value, $serialize = true){
// 处理之前进行一步验证
return $this->getEncrypter()->encrypt($value, $serialize);
}
/*
* 直接加密
*/
public function encryptString($value)
{
return $this->getEncrypter()->encrypt($value, false);
}
/*
* 定义公开的解密函数
*/
public function decrypt($payload, $unserialize = true){
// 处理之前进行一步验证
return $this->getEncrypter()->decrypt($payload, $unserialize);
}
/*
* 直接解密
*/
public function decryptString($payload)
{
return $this->getEncrypter()->decrypt($payload, false);
}
/**
* Determine if the given key and cipher combination is valid.
*
* @param string $key
* @param string $cipher
* @return bool
*/
private function supported($key, $cipher)
{
$length = mb_strlen($key, '8bit');
return ($cipher === 'AES-128-CBC' && $length === 16) ||
($cipher === 'AES-256-CBC' && $length === 32);
}
/*
* 加密之前的验证
*/
private function getEncrypter(){
if (Str::startsWith($this->key, 'base64:')) {
$this->key = base64_decode(substr($this->key, 7));
}
$this->key = (string) $this->key;
if (!static::supported($this->key, $this->cipher)) {
throw new RuntimeException('The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.');
}
return new Encrypter($this->key, $this->cipher);
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
不是直接有这个嘛,直接生成,php artisan make:generate ,解密的时候对应APP_KEY值解密就好了
@Q956569011 “key 值多变”,一个网站同时维护多个key值,你生成这个key值,前一个key值就失效了