543 lines
14 KiB
PHP
Executable File
543 lines
14 KiB
PHP
Executable File
<?php
|
||
|
||
include './SecurityUtil.php';
|
||
include './SecretGetRequest.php';
|
||
include './TopSdkFeedbackUploadRequest.php';
|
||
include './iCache.php';
|
||
include '../../TopSdk.php';
|
||
|
||
class SecurityClient
|
||
{
|
||
private $topClient ;
|
||
private $randomNum ;
|
||
private $securityUtil;
|
||
private $cacheClient = null;
|
||
|
||
function __construct($client, $random)
|
||
{
|
||
|
||
define('APP_SECRET_TYPE','2');
|
||
define('APP_USER_SECRET_TYPE','3');
|
||
|
||
$this->topClient = $client;
|
||
$this->randomNum = $random;
|
||
$this->securityUtil = new SecurityUtil();
|
||
}
|
||
|
||
/**
|
||
* 设置缓存处理器
|
||
*/
|
||
function setCacheClient($cache)
|
||
{
|
||
$this->cacheClient = $cache;
|
||
}
|
||
|
||
/**
|
||
* 密文检索,在秘钥升级场景下兼容查询
|
||
*
|
||
* @see #search(String, String, String, Long)
|
||
* @return
|
||
*/
|
||
function searchPrevious($data,$type,$session = null)
|
||
{
|
||
return $this->searchInner($data,$type,$session,-1);
|
||
}
|
||
|
||
/**
|
||
* 密文检索(每个用户单独分配秘钥)
|
||
*
|
||
* @see #search(String, String, String, Long)
|
||
* @return
|
||
*/
|
||
function search($data,$type,$session = null)
|
||
{
|
||
return $this->searchInner($data,$type,$session,null);
|
||
}
|
||
|
||
/**
|
||
* 密文检索。 手机号码格式:$base64(H-MAC(phone后4位))$ simple格式:base64(H-MAC(滑窗))
|
||
*
|
||
* @param data
|
||
* 明文数据
|
||
* @param type
|
||
* 加密字段类型(例如:simple\phone)
|
||
* @param session
|
||
* 用户身份,用户级加密必填
|
||
* @param version
|
||
* 秘钥历史版本
|
||
* @return
|
||
*/
|
||
function searchInner($data, $type, $session,$version)
|
||
{
|
||
if(empty($data) || empty($type)){
|
||
return $data;
|
||
}
|
||
|
||
$secretContext = null;
|
||
|
||
$secretContext = $this->callSecretApiWithCache($session,$version);
|
||
$this->incrCounter(3,$type,$secretContext,true);
|
||
|
||
if(empty($secretContext) || empty($secretContext->secret)) {
|
||
return $data;
|
||
}
|
||
|
||
return $this->securityUtil->search($data, $type,$secretContext);
|
||
}
|
||
|
||
|
||
/**
|
||
* 单条数据解密,使用appkey级别公钥
|
||
* 非加密数据直接返回原文
|
||
*/
|
||
function decryptPublic($data,$type)
|
||
{
|
||
return $this->decrypt($data,$type,null);
|
||
}
|
||
/**
|
||
* 单条数据解密
|
||
* 非加密数据直接返回原文
|
||
*/
|
||
function decrypt($data,$type,$session)
|
||
{
|
||
if(empty($data) || empty($type)){
|
||
return $data;
|
||
}
|
||
$secretData = $this->securityUtil->getSecretDataByType($data,$type);
|
||
if(empty($secretData)){
|
||
return $data;
|
||
}
|
||
|
||
if($this->securityUtil->isPublicData($data,$type)){
|
||
$secretContext = $this->callSecretApiWithCache(null,$secretData->secretVersion);
|
||
} else {
|
||
$secretContext = $this->callSecretApiWithCache($session,$secretData->secretVersion);
|
||
}
|
||
$this->incrCounter(2,$type,$secretContext,true);
|
||
|
||
return $this->securityUtil->decrypt($data,$type,$secretContext);
|
||
}
|
||
|
||
/**
|
||
* 多条数据解密,使用appkey级别公钥
|
||
* 非加密数据直接返回原文
|
||
*/
|
||
function decryptBatchPublic($array,$type)
|
||
{
|
||
if(empty($array) || empty($type)){
|
||
return null;
|
||
}
|
||
|
||
$result = array();
|
||
foreach ($array as $value) {
|
||
$secretData = $this->securityUtil->getSecretDataByType($value,$type);
|
||
$secretContext = $this->callSecretApiWithCache(null,$secretData->secretVersion);
|
||
|
||
if(empty($secretData)){
|
||
$result[$value] = $value;
|
||
}else{
|
||
$result[$value] = $this->securityUtil->decrypt($value,$type,$secretContext);
|
||
$this->incrCounter(2,$type,$secretContext,true);
|
||
}
|
||
|
||
$this->flushCounter($secretContext);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 多条数据解密,必须是同一个type和用户,返回结果是 KV结果
|
||
* 非加密数据直接返回原文
|
||
*/
|
||
function decryptBatch($array,$type,$session)
|
||
{
|
||
if(empty($array) || empty($type)){
|
||
return null;
|
||
}
|
||
|
||
$result = array();
|
||
foreach ($array as $value) {
|
||
$secretData = $this->securityUtil->getSecretDataByType($value,$type);
|
||
if(empty($secretData)){
|
||
$result[$value] = $value;
|
||
} else if($this->securityUtil->isPublicData($value,$type)){
|
||
$appContext = $this->callSecretApiWithCache(null,$secretData->secretVersion);
|
||
$result[$value] = $this->securityUtil->decrypt($value,$type,$appContext);
|
||
$this->incrCounter(2,$type,$appContext,false);
|
||
$this->flushCounter($appContext);
|
||
} else {
|
||
$secretContext = $this->callSecretApiWithCache($session,$secretData->secretVersion);
|
||
$result[$value] = $this->securityUtil->decrypt($value,$type,$secretContext);
|
||
$this->incrCounter(2,$type,$secretContext,false);
|
||
$this->flushCounter($secretContext);
|
||
}
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 使用上一版本秘钥解密,app级别公钥
|
||
*/
|
||
function decryptPreviousPublic($data,$type)
|
||
{
|
||
$secretContext = $this->callSecretApiWithCache(null,-1);
|
||
return $this->securityUtil->decrypt($data,$type,$secretContext);
|
||
}
|
||
/**
|
||
* 使用上一版本秘钥解密,一般只用于更新秘钥
|
||
*/
|
||
function decryptPrevious($data,$type,$session)
|
||
{
|
||
if($this->securityUtil->isPublicData($data,$type)){
|
||
$secretContext = $this->callSecretApiWithCache(null,-1);
|
||
} else {
|
||
$secretContext = $this->callSecretApiWithCache($session,-1);
|
||
}
|
||
return $this->securityUtil->decrypt($data,$type,$secretContext);
|
||
}
|
||
|
||
/**
|
||
* 加密单条数据,使用app级别公钥
|
||
*/
|
||
function encryptPublic($data,$type,$version = null)
|
||
{
|
||
return $this->encrypt($data,$type,null,$version);
|
||
}
|
||
/**
|
||
* 加密单条数据
|
||
*/
|
||
function encrypt($data,$type,$session = null,$version = null)
|
||
{
|
||
if(empty($data) || empty($type)){
|
||
return null;
|
||
}
|
||
$secretContext = $this->callSecretApiWithCache($session,null);
|
||
$this->incrCounter(1,$type,$secretContext,true);
|
||
|
||
return $this->securityUtil->encrypt($data,$type,$version,$secretContext);
|
||
}
|
||
|
||
/**
|
||
* 加密多条数据,使用app级别公钥
|
||
*/
|
||
function encryptBatchPublic($array,$type,$version = null)
|
||
{
|
||
if(empty($array) || empty($type)){
|
||
return null;
|
||
}
|
||
$secretContext = $this->callSecretApiWithCache(null,null);
|
||
$result = array();
|
||
foreach ($array as $value) {
|
||
$result[$value] = $this->securityUtil->encrypt($value,$type,$version,$secretContext);
|
||
$this->incrCounter(1,$type,$secretContext,false);
|
||
}
|
||
$this->flushCounter($secretContext);
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 加密多条数据,必须是同一个type和用户,返回结果是 KV结果
|
||
*/
|
||
function encryptBatch($array,$type,$session,$version = null)
|
||
{
|
||
if(empty($array) || empty($type)){
|
||
return null;
|
||
}
|
||
$secretContext = $this->callSecretApiWithCache($session,null);
|
||
$result = array();
|
||
foreach ($array as $value) {
|
||
$result[$value] = $this->securityUtil->encrypt($value,$type,$version,$secretContext);
|
||
$this->incrCounter(1,$type,$secretContext,false);
|
||
}
|
||
$this->flushCounter($secretContext);
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 使用上一版本秘钥加密,使用app级别公钥
|
||
*/
|
||
function encryptPreviousPublic($data,$type)
|
||
{
|
||
$secretContext = $this->callSecretApiWithCache(null,-1);
|
||
$this->incrCounter(1,$type,$secretContext,true);
|
||
|
||
return $this->securityUtil->encrypt($data,$type,$secretContext->version,$secretContext);
|
||
}
|
||
/**
|
||
* 使用上一版本秘钥加密,一般只用于更新秘钥
|
||
*/
|
||
function encryptPrevious($data,$type,$session)
|
||
{
|
||
$secretContext = $this->callSecretApiWithCache($session,-1);
|
||
$this->incrCounter(1,$type,$secretContext,true);
|
||
|
||
return $this->securityUtil->encrypt($data,$type,$secretContext);
|
||
}
|
||
|
||
/**
|
||
* 根据session生成秘钥
|
||
*/
|
||
function initSecret($session)
|
||
{
|
||
return $this->callSecretApiWithCache($session,null);
|
||
}
|
||
|
||
function buildCacheKey($session,$secretVersion)
|
||
{
|
||
if(empty($session)){
|
||
return $this->topClient->getAppkey();
|
||
}
|
||
if(empty($secretVersion)){
|
||
return $session ;
|
||
}
|
||
return $session.'_'.$secretVersion ;
|
||
}
|
||
|
||
|
||
function generateCustomerSession($userId)
|
||
{
|
||
return '_'.$userId ;
|
||
}
|
||
|
||
/**
|
||
* 判断是否是已加密的数据
|
||
*/
|
||
function isEncryptData($data,$type)
|
||
{
|
||
if(empty($data) || empty($type)){
|
||
return false;
|
||
}
|
||
return $this->securityUtil->isEncryptData($data,$type);
|
||
}
|
||
|
||
/**
|
||
* 判断是否是已加密的数据,数据必须是同一个类型
|
||
*/
|
||
function isEncryptDataArray($array,$type)
|
||
{
|
||
if(empty($array) || empty($type)){
|
||
return false;
|
||
}
|
||
return $this->securityUtil->isEncryptDataArray($array,$type);
|
||
}
|
||
|
||
/**
|
||
* 判断数组中的数据是否存在密文,存在任何一个返回true,否则false
|
||
*/
|
||
function isPartEncryptData($array,$type)
|
||
{
|
||
if(empty($array) || empty($type)){
|
||
return false;
|
||
}
|
||
return $this->securityUtil->isPartEncryptData($array,$type);
|
||
}
|
||
|
||
/**
|
||
* 获取秘钥,使用缓存
|
||
*/
|
||
function callSecretApiWithCache($session,$secretVersion)
|
||
{
|
||
if($this->cacheClient)
|
||
{
|
||
$time = time();
|
||
$cacheKey = $this->buildCacheKey($session,$secretVersion);
|
||
$secretContext = $this->cacheClient->getCache($cacheKey);
|
||
|
||
if($secretContext)
|
||
{
|
||
if($this->canUpload($secretContext)){
|
||
if($this->report($secretContext)){
|
||
$this->clearReport($secretContext);
|
||
}
|
||
}
|
||
}
|
||
|
||
if($secretContext && $secretContext->invalidTime > $time)
|
||
{
|
||
return $secretContext;
|
||
}
|
||
}
|
||
|
||
$secretContext = $this->callSecretApi($session,$secretVersion);
|
||
|
||
if($this->cacheClient)
|
||
{
|
||
$secretContext->cacheKey = $cacheKey;
|
||
$this->cacheClient->setCache($cacheKey,$secretContext);
|
||
}
|
||
|
||
return $secretContext;
|
||
}
|
||
|
||
function incrCounter($op,$type,$secretContext,$flush)
|
||
{
|
||
if($op == 1){
|
||
switch ($type) {
|
||
case 'nick':
|
||
$secretContext->encryptNickNum ++ ;
|
||
break;
|
||
case 'simple':
|
||
$secretContext->encryptSimpleNum ++ ;
|
||
break;
|
||
case 'receiver_name':
|
||
$secretContext->encryptReceiverNameNum ++ ;
|
||
break;
|
||
case 'phone':
|
||
$secretContext->encryptPhoneNum ++ ;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}else if($op == 2){
|
||
switch ($type) {
|
||
case 'nick':
|
||
$secretContext->decryptNickNum ++ ;
|
||
break;
|
||
case 'simple':
|
||
$secretContext->decryptSimpleNum ++ ;
|
||
break;
|
||
case 'receiver_name':
|
||
$secretContext->decryptReceiverNameNum ++ ;
|
||
break;
|
||
case 'phone':
|
||
$secretContext->decryptPhoneNum ++ ;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}else{
|
||
switch ($type) {
|
||
case 'nick':
|
||
$secretContext->searchNickNum ++ ;
|
||
break;
|
||
case 'simple':
|
||
$secretContext->searchSimpleNum ++ ;
|
||
break;
|
||
case 'receiver_name':
|
||
$secretContext->searchReceiverNameNum ++ ;
|
||
break;
|
||
case 'phone':
|
||
$secretContext->searchPhoneNum ++ ;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
if($flush && $this->cacheClient){
|
||
$this->cacheClient->setCache($secretContext->cacheKey,$secretContext);
|
||
}
|
||
}
|
||
|
||
function flushCounter($secretContext)
|
||
{
|
||
if($this->cacheClient){
|
||
$this->cacheClient->setCache($secretContext->cacheKey,$secretContext);
|
||
}
|
||
}
|
||
|
||
function clearReport($secretContext)
|
||
{
|
||
$secretContext->encryptPhoneNum = 0;
|
||
$secretContext->encryptNickNum = 0;
|
||
$secretContext->encryptReceiverNameNum = 0;
|
||
$secretContext->encryptSimpleNum = 0;
|
||
$secretContext->encryptSearchNum = 0;
|
||
$secretContext->decryptPhoneNum = 0;
|
||
$secretContext->decryptNickNum = 0;
|
||
$secretContext->decryptReceiverNameNum = 0;
|
||
$secretContext->decryptSimpleNum = 0;
|
||
$secretContext->decryptSearchNum = 0;
|
||
$secretContext->searchPhoneNum = 0;
|
||
$secretContext->searchNickNum = 0;
|
||
$secretContext->searchReceiverNameNum = 0;
|
||
$secretContext->searchSimpleNum = 0;
|
||
$secretContext->searchSearchNum = 0;
|
||
$secretContext->lastUploadTime = time();
|
||
}
|
||
|
||
function canUpload($secretContext)
|
||
{
|
||
$current = time();
|
||
if($current - $secretContext->lastUploadTime > 300){
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* 上报信息
|
||
*/
|
||
function report($secretContext)
|
||
{
|
||
$request = new TopSdkFeedbackUploadRequest;
|
||
$request->setContent($secretContext->toLogString());
|
||
|
||
if(empty($secretContext->session)){
|
||
$request->setType(APP_SECRET_TYPE);
|
||
}else{
|
||
$request->setType(APP_USER_SECRET_TYPE);
|
||
}
|
||
|
||
$response = $this->topClient->execute($request,$secretContext->session);
|
||
if($response->code == 0){
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 获取秘钥,不使用缓存
|
||
*/
|
||
function callSecretApi($session,$secretVersion)
|
||
{
|
||
$request = new TopSecretGetRequest;
|
||
$request->setRandomNum($this->randomNum);
|
||
if($secretVersion)
|
||
{
|
||
if(intval($secretVersion) < 0 || $session == null){
|
||
$session = null;
|
||
$secretVersion = -1 * intval($secretVersion < 0);
|
||
}
|
||
$request->setSecretVersion($secretVersion);
|
||
}
|
||
|
||
$topSession = $session;
|
||
if($session != null && $session[0] == '_')
|
||
{
|
||
$request->setCustomerUserId(substr($session,1));
|
||
$topSession = null;
|
||
}
|
||
|
||
$response = $this->topClient->execute($request,$topSession);
|
||
if($response->code != 0){
|
||
throw new Exception($response->msg);
|
||
}
|
||
|
||
$time = time();
|
||
$secretContext = new SecretContext();
|
||
$secretContext->maxInvalidTime = $time + intval($response->max_interval);
|
||
$secretContext->invalidTime = $time + intval($response->interval);
|
||
$secretContext->secret = strval($response->secret);
|
||
$secretContext->session = $session;
|
||
if(!empty($response->app_config)){
|
||
$tmpJson = json_decode($response->app_config);
|
||
$appConfig = array();
|
||
foreach ($tmpJson as $key => $value){
|
||
$appConfig[$key] = $value;
|
||
}
|
||
$secretContext->appConfig = $appConfig;
|
||
}
|
||
|
||
if(empty($session)){
|
||
$secretContext->secretVersion = -1 * intval($response->secret_version);
|
||
}else{
|
||
$secretContext->secretVersion = intval($response->secret_version);
|
||
}
|
||
return $secretContext;
|
||
}
|
||
}
|
||
?>
|