using System;
|
using System.Collections.Generic;
|
using Top.Api.Util;
|
using System.Threading;
|
using Top.Api.Report;
|
|
|
namespace Top.Api.Security
|
{
|
|
/// <summary>
|
/// 加、解密核心类
|
/// </summary>
|
public class SecurityCore : SecurityConstants
|
{
|
private static readonly ITopLogger Log = Top.Api.Log.Instance;
|
// 缓存用户单独分配秘钥,需要加同步锁
|
private static readonly IDictionary<string, SecretContext> AppUserSecretCache = new Dictionary<string, SecretContext>();
|
private static readonly IDictionary<string, SecretContext> AppSecretCache = new Dictionary<string, SecretContext>();
|
private static readonly object EmptyObject = new object();
|
private static readonly object CacheLock = new object();
|
private static readonly object AppLock = new object();
|
private static readonly object AsynQueueKeyLock = new object();
|
|
private string randomNum;// 伪随机码
|
private DefaultTopClient topClient;
|
private static IDictionary<string, object> asynQueueKey = new Dictionary<string, object>();
|
private static readonly IDictionary<string, IDictionary<string, object>> AllAppConfig = new Dictionary<string, IDictionary<string, object>>();
|
|
private bool streetest;
|
|
public static IDictionary<string, SecretContext> GetAppUserSecretCache()
|
{
|
return AppUserSecretCache;
|
}
|
|
/// <summary>
|
/// 判断密文是否支持检索
|
/// </summary>
|
/// <param name="key"></param>
|
/// <param name="version"></param>
|
/// <returns></returns>
|
public bool IsIndexEncrypt(string key, Nullable<Int64> version)
|
{
|
if (version != null && version < 0)
|
{
|
key = PREVIOUS + key;
|
}
|
else
|
{
|
key = CURRENT + key;
|
}
|
|
IDictionary<string, Object> appConfig = getAppConfig();
|
|
if (appConfig == null)
|
{
|
return false;
|
}
|
|
object encryptType = null;
|
appConfig.TryGetValue(key, out encryptType);
|
|
return INDEX_ENCRYPT_TYPE.Equals(encryptType);
|
}
|
|
private IDictionary<string, Object> getAppConfig()
|
{
|
IDictionary<string, Object> appConfig = null;
|
AllAppConfig.TryGetValue(topClient.appKey, out appConfig);
|
return appConfig;
|
}
|
|
/// <summary>
|
/// 获取压缩长度
|
/// </summary>
|
/// <returns></returns>
|
public int GetCompressLen()
|
{
|
IDictionary<string, Object> appConfig = getAppConfig();
|
if (appConfig != null)
|
{
|
object compressLen = null;
|
appConfig.TryGetValue(ENCRYPT_INDEX_COMPRESS_LEN, out compressLen);
|
|
if (compressLen != null)
|
{
|
return Convert.ToInt32(compressLen);
|
}
|
}
|
return DEFAULT_INDEX_ENCRYPT_COMPRESS_LEN;
|
}
|
|
/// <summary>
|
/// 获取滑动窗口大小
|
/// </summary>
|
/// <returns></returns>
|
public int GetSlideSize()
|
{
|
IDictionary<string, Object> appConfig = getAppConfig();
|
if (appConfig != null)
|
{
|
object encryptSlideSize = null;
|
appConfig.TryGetValue(ENCRYPT_SLIDE_SIZE, out encryptSlideSize);
|
if (encryptSlideSize != null)
|
{
|
return Convert.ToInt32(encryptSlideSize);
|
}
|
}
|
return DEFAULT_ENCRYPT_SLIDE_SIZE;
|
}
|
|
public SecurityCore(DefaultTopClient topClient, string randomNum, bool streetest)
|
{
|
this.streetest = streetest;
|
this.topClient = topClient;
|
this.randomNum = randomNum;
|
// 初始化报表
|
ApiReporter apiReporter = new ApiReporter();
|
apiReporter.InitSecret(topClient);
|
}
|
|
public void SetRandomNum(string randomNum)
|
{
|
this.randomNum = randomNum;
|
}
|
|
/// <summary>
|
/// 获取秘钥
|
/// </summary>
|
/// <param name="session"></param>
|
/// <param name="secretVersion"></param>
|
/// <returns></returns>
|
public SecretContext GetSecret(string session, Nullable<Int64> secretVersion)
|
{
|
SecretContext secretContext = GetSecret(session, GenerateSecretKey(session, secretVersion));
|
if (secretContext != null)
|
{
|
if (secretContext.IsValid())
|
{
|
return secretContext;
|
}
|
|
if (secretContext.IsMaxValid())
|
{
|
// 异步更新秘钥
|
AsynUpdateSecret(session, secretVersion);
|
return secretContext;
|
}
|
|
string cacheKey = GenerateSecretKey(session, secretVersion);
|
lock (CacheLock)
|
{
|
if (session != null)
|
{
|
AppUserSecretCache.Remove(cacheKey);
|
}
|
else
|
{
|
AppSecretCache.Remove(cacheKey);
|
}
|
}
|
// 同步调用获取秘钥
|
return CallSecretApi(session, secretVersion);
|
}
|
else
|
{
|
// 同步调用获取秘钥
|
return CallSecretApi(session, secretVersion);
|
}
|
}
|
|
private string GenerateSecretKey(string session, Nullable<Int64> secretVersion)
|
{
|
if (session == null)
|
{
|
return this.topClient.appKey;
|
}
|
if (secretVersion == null)
|
{
|
return session;
|
}
|
|
return session + "_" + secretVersion;
|
}
|
|
/// <summary>
|
/// 从本地获取秘钥信息
|
/// </summary>
|
/// <param name="session"></param>
|
/// <param name="cacheKey"></param>
|
/// <returns></returns>
|
private SecretContext GetSecret(string session, string cacheKey)
|
{
|
SecretContext secretContext;
|
if (session != null)
|
{
|
AppUserSecretCache.TryGetValue(cacheKey, out secretContext);
|
}
|
else
|
{
|
AppSecretCache.TryGetValue(cacheKey, out secretContext);
|
}
|
return secretContext;
|
}
|
|
/// <summary>
|
/// 调用获取秘钥api
|
/// </summary>
|
/// <param name="session"></param>
|
/// <param name="secretVersion"></param>
|
/// <returns></returns>
|
private SecretContext CallSecretApi(string session, Nullable<Int64> secretVersion)
|
{
|
// 获取伪随机码
|
if (string.IsNullOrEmpty(randomNum))
|
{
|
throw new ArgumentException("randomNum can`t be empty");
|
}
|
|
TopSecretGetRequest request = new TopSecretGetRequest();
|
request.RandomNum = randomNum;
|
request.SecretVersion = secretVersion;
|
if (streetest)
|
{
|
request.AddOtherParameter("tb_eagleeyex_t", "1");
|
}
|
|
TopSecretGetResponse response;
|
if (session != null && session.StartsWith(UNDERLINE))
|
{
|
string customerUserId = session.Substring(1);
|
if (!StringUtil.IsDigits(customerUserId))
|
{
|
throw new ArgumentException("session invalid");
|
}
|
request.CustomerUserId = Convert.ToInt64(customerUserId);
|
response = topClient.Execute(request, null);
|
}
|
else
|
{
|
response = topClient.Execute(request, session);
|
}
|
if (!response.IsError)
|
{
|
|
IDictionary<string, Object> appConfig = null;
|
if (!string.IsNullOrEmpty(response.AppConfig))
|
{
|
appConfig = (IDictionary<string, Object>)TopUtils.JsonToObject(response.AppConfig);
|
putAppConfig(appConfig);
|
}
|
SecretContext secretContext = new SecretContext();
|
if (response.Secret != null)
|
{
|
long currentTime = TopUtils.GetCurrentTimeMillis();
|
secretContext.InvalidTime = currentTime + (response.Interval * 1000);
|
secretContext.MaxInvalidTime = (currentTime + (response.MaxInterval * 1000));
|
secretContext.Secret = Convert.FromBase64String(response.Secret);
|
secretContext.SecretVersion = response.SecretVersion;
|
}
|
else
|
{
|
if (appConfig != null)
|
{
|
object publishStatus = null;
|
appConfig.TryGetValue(PUBLISH_STATUS, out publishStatus);
|
if (BETA_STATUS.Equals(publishStatus))
|
{
|
// 设置空缓存
|
SetNullCache(secretContext);
|
}
|
}
|
}
|
|
PutToCache(session, secretVersion, secretContext);
|
return secretContext;
|
}
|
else
|
{
|
// 查找不到历史秘钥
|
if ("20005".Equals(response.SubErrCode))
|
{
|
SecretContext secretContext = new SecretContext();
|
// 设置空缓存
|
SetNullCache(secretContext);
|
|
PutToCache(session, secretVersion, secretContext);
|
return secretContext;
|
}
|
throw new SecretException(response.ErrCode, response.ErrMsg, response.SubErrCode, response.SubErrMsg);
|
}
|
}
|
|
private void putAppConfig(IDictionary<string, Object> appConfig)
|
{
|
lock (AppLock)
|
{
|
if (!AllAppConfig.ContainsKey(topClient.appKey))
|
{
|
AllAppConfig.Add(topClient.appKey, appConfig);
|
}
|
}
|
}
|
|
private void PutToCache(string session, Nullable<Int64> secretVersion, SecretContext secretContext)
|
{
|
string cacheKey = GenerateSecretKey(session, secretVersion);
|
|
lock (CacheLock)
|
{
|
if (session != null)
|
{
|
if (AppUserSecretCache.ContainsKey(cacheKey))
|
{
|
AppUserSecretCache[cacheKey] = secretContext;
|
}
|
else
|
{
|
AppUserSecretCache.Add(cacheKey, secretContext);
|
}
|
}
|
else
|
{
|
if (AppSecretCache.ContainsKey(cacheKey))
|
{
|
AppSecretCache[cacheKey] = secretContext;
|
}
|
else
|
{
|
AppSecretCache.Add(cacheKey, secretContext);
|
}
|
}
|
}
|
}
|
|
/// <summary>
|
/// 设置空缓存
|
/// </summary>
|
/// <param name="secretContext"></param>
|
private void SetNullCache(SecretContext secretContext)
|
{
|
long currentTime = TopUtils.GetCurrentTimeMillis();
|
secretContext.InvalidTime = currentTime + (DEFAULT_INTERVAL * 1000);
|
secretContext.MaxInvalidTime = currentTime + (DEFAULT_MAX_INTERVAL * 1000);
|
}
|
|
/// <summary>
|
/// 异步更新秘钥
|
/// </summary>
|
/// <param name="session"></param>
|
/// <param name="secretVersion"></param>
|
private void AsynUpdateSecret(string session, Nullable<Int64> secretVersion)
|
{
|
string cacheKey = GenerateSecretKey(session, secretVersion);
|
|
lock (AsynQueueKeyLock)
|
{
|
// 不需要重复提交秘钥请求
|
if (asynQueueKey.ContainsKey(cacheKey))
|
{
|
return;
|
}
|
SecretContext secretContext = GetSecret(session, GenerateSecretKey(session, secretVersion));
|
if (secretContext != null && secretContext.IsValid())
|
{
|
return;
|
}
|
|
asynQueueKey.Add(cacheKey, EmptyObject);
|
}
|
|
|
WaitCallback secretApiCallback = (state) =>
|
{
|
try
|
{
|
CallSecretApi(session, secretVersion);
|
}
|
catch (Exception e)
|
{
|
Log.Error(string.Format("asyn update secret error: {0}", e.Message));
|
|
}
|
finally
|
{
|
lock (AsynQueueKeyLock)
|
{
|
asynQueueKey.Remove(cacheKey);
|
}
|
}
|
};
|
|
try
|
{
|
ThreadPool.QueueUserWorkItem(secretApiCallback);
|
}
|
catch (Exception e)
|
{
|
lock (AsynQueueKeyLock)
|
{
|
asynQueueKey.Remove(cacheKey);
|
}
|
Log.Error(string.Format("add QueueUserWorkItem error: {0}", e.Message));
|
}
|
|
}
|
|
|
}
|
}
|