需求分析
我們有一臺總的資料處理伺服器,又有很多臺資料來源伺服器。為了讓他們相互通訊,我們在每個伺服器上搭建了相同的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 協議》,轉載必須註明作者和本文連結