利用yii2實現項目授權認證

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和同聲傳譯服務需要調度,這裏是爲後面的業務做唯一性。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章