1.準備一個基本參數的文本,這裏爲licence.info
{
"permittedFor": "00:0c:29:f0:03:4d", //頒發單位,這裏爲機器mac地址
"expiredAt":"", //過期時間
"pptLimit":0, //購買的PPT識別服務的使用次數(這裏具體情況而定,業務不同)
"voiceLimit":0, //購買的同聲傳譯識別服務的使用次數(這裏具體情況而定,業務不同)
"issuedAt":"2020/2/18 12:00:00", //頒發時間
"issuedBy":"xxxx.com" //頒發單位
}
註釋:這裏爲具體用戶的購買情況的文本,我們記錄下來,因爲會根據參數來生成對應的密鑰key。
2.準備一個空文件key.lic用來存放密鑰
3.準備一個空文件licence.lic來存方加密的文本信息
如圖:
4.在console下新建控制器ReduceController繼承自\yii\console\Controller
public function actionEncrypt()
{
// $data = [
// 'permittedFor' => \Yii::$app->helper->getMAC(),//頒發給的機器mac,可以數組或字符串
// 'expiredAt' => date('Y-m-d H:i:s'),//過期時間
// 'pptLimit' => 30,//ppt任務數限制
// 'voiceLimit' => 20,//同聲傳譯任務數限制
// 'issuedAt' => date('Y-m-d H:i:s'),//頒發時間
// 'issuedBy' => 'xxxx.cn',//頒發單位
// 'id'=>\Yii::$app->getSecurity()->generateRandomString()
// ];
$licenceDir = ArrayHelper::getValue(Yii::$app->params, 'licenceFiles');
if (empty($licenceDir)) {
$licenceDir = '.';
}
$keyFile = $licenceDir . DIRECTORY_SEPARATOR . 'key.lic';
$licenceFile = $licenceDir . DIRECTORY_SEPARATOR . 'licence.lic';
$registerFile = $licenceDir . DIRECTORY_SEPARATOR . 'licence.info';
$encryptString = file_get_contents($registerFile);
$encryptData = json_decode($encryptString, true);
$encryptData['id'] = Yii::$app->getSecurity()->generateRandomString();
$encryptString = json_encode($encryptData);
$key = md5($encryptString);
$encrypt = Yii::$app->getSecurity()->encryptByKey($encryptString, $key);
file_put_contents($licenceFile, $encrypt);
file_put_contents($keyFile, $key);
$this->stdout('done');
$this->stdout(PHP_EOL);
return ExitCode::OK;
}
建立好後我們需要啓動命令,生成一個licence.lic和key.lic文件,這是加密後的密文和解密的密鑰
下面開始解密授權:
<?php
namespace common\components;
use yii\base\BaseObject;
use yii\helpers\ArrayHelper;
class Licence extends BaseObject
{
private $licenceFile;
private $keyFile;
private $registerFile;
private $key;
private $licence;
private $macAddresses;
private $taskCount = [];
public function init()
{
parent::init(); // TODO: Change the autogenerated stub
$licenceDir = ArrayHelper::getValue(\Yii::$app->params, 'licenceFiles');
if (empty($licenceDir)) {
$licenceDir = '../..';
}
$this->keyFile = $licenceDir . DIRECTORY_SEPARATOR . 'key.lic';
$this->licenceFile = $licenceDir . DIRECTORY_SEPARATOR . 'licence.lic';
$this->registerFile = \Yii::getAlias('@common') . DIRECTORY_SEPARATOR . 'register.info';
if (!file_exists($this->keyFile)) {
throw new \Exception('授權文件缺失');
}
if (!file_exists($this->licenceFile)) {
throw new \Exception('授權文件缺失');
}
$key = file_get_contents($this->keyFile);
$licence = file_get_contents($this->licenceFile);
$encryptedString = \Yii::$app->getSecurity()->decryptByKey($licence, $key);
if (empty($encryptedString)) {
throw new \Exception('授權licence無效');
}
$licenceData = json_decode($encryptedString, true);
if (empty($licenceData)) {
throw new \Exception('授權licence無效');
}
if (empty($licenceData['id'])) {
throw new \Exception('授權licence格式無效');
}
if (empty($licenceData['issuedAt'])) {
throw new \Exception('授權licence格式無效');
}
if (!isset($licenceData['expiredAt'])) {
throw new \Exception('授權licence格式無效');
}
if (!isset($licenceData['permittedFor'])) {
throw new \Exception('授權licence格式無效');
}
if (!isset($licenceData['pptLimit'])) {
throw new \Exception('授權licence格式無效');
}
if (!isset($licenceData['voiceLimit'])) {
throw new \Exception('授權licence格式無效');
}
$issuedAt = strtotime(ArrayHelper::getValue($licenceData, 'issuedAt'));
$now = time();
if ($now < $issuedAt) {
throw new \Exception('licence生成時間錯誤,授權驗證失敗');
}
$expiredAt = ArrayHelper::getValue($licenceData, 'expiredAt');
if (!empty($expiredAt)) {
$expiredAtTime = strtotime($expiredAt);
if ($now >= $expiredAtTime) {
throw new \Exception('授權licence已過期,授權驗證失敗');
}
}
$permittedFor = ArrayHelper::getValue($licenceData, 'permittedFor');
if (!empty($permittedFor)) {
$macAddress = $this->getMAC();
if (empty($macAddress)) {
throw new \Exception('無法獲取服務器mac地址,授權驗證失敗');
}
$this->macAddresses = $macAddress;
if (is_array($permittedFor)) {
$intersect = array_intersect($permittedFor, $macAddress);
if (empty($intersect)) {
throw new \Exception('服務器mac地址不匹配,授權驗證失敗');
}
} else {
if (!in_array($permittedFor, $macAddress)) {
throw new \Exception('服務器mac地址不匹配,授權驗證失敗');
}
}
}
$this->licence = $licenceData;
$this->key = $key;
}
private function getMAC()
{
$return_array = [];
switch (strtolower(PHP_OS)) {
case "linux":
$return_array = $this->forLinux();
break;
case "unix":
case "aix":
case "solaris":
break;
default:
$return_array = $this->forWindows();
break;
}
$mac_addr = [];
$temp_array = array();
foreach ($return_array as $value) {
if (preg_match("/[0-9a-f][0-9a-f][:-]" . "[0-9a-f][0-9a-f][:-]" . "[0-9a-f][0-9a-f][:-]" . "[0-9a-f][0-9a-f][:-]" . "[0-9a-f][0-9a-f][:-]" . "[0-9a-f][0-9a-f]/i", $value, $temp_array)) {
$mac_addr[] = $temp_array[0];
}
}
unset($temp_array);
return $mac_addr;
}
private function forWindows()
{
if (!function_exists('exec')) {
throw new \Exception('exec方法不可用,無法獲取服務器mac地址');
}
@exec("ipconfig /all", $return_array);
if ($return_array)
return $return_array;
else {
$ipconfig = $_SERVER["WINDIR"] . "system32ipconfig.exe";
if (is_file($ipconfig))
@exec($ipconfig . " /all", $return_array);
else
@exec($_SERVER["WINDIR"] . "systemipconfig.exe /all", $return_array);
return $return_array;
}
}
private function forLinux()
{
if (!function_exists('exec')) {
throw new \Exception('exec方法不可用,無法獲取服務器mac地址');
}
@exec('ifconfig -a', $output, $returnArray);
if ($returnArray === 0) {
return $output;
}
@exec("/usr/sbin/ifconfig -a", $output, $returnArray);
if ($returnArray === 0) {
return $output;
}
throw new \Exception('無法獲取服務器mac地址,exec狀態碼:' . $returnArray);
}
/**
* @return mixed
* @throws \Exception
*/
public function getMacAddress()
{
if (empty($this->macAddresses)) {
$this->macAddresses = $this->getMAC();
if (empty($this->macAddresses)) {
throw new \Exception('獲取服務器mac地址失敗');
}
}
$macAddress = reset($this->macAddresses);;
return $macAddress;
}
}
我們重寫了yii\base\BaseObject的init方法,所以會在項目初始化去驗證init內容
如果key.lic和licence.lic能夠匹配上,通過\Yii::$app->getSecurity()->decryptByKey($licence, $key);這一串代碼,那麼就實現了授權。爲什麼需要mac地址,因爲這裏佈置在多個的docker容器裏面,ppt和同聲傳譯服務需要調度,這裏是爲後面的業務做唯一性。