456 lines
15 KiB
C#
456 lines
15 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Runtime.CompilerServices;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using AOT;
|
||
using NetEase;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using NTESHTPSec;
|
||
using UnityEngine;
|
||
using UnityEngine.Android;
|
||
using AntiCheatResult = NetEase.AntiCheatResult;
|
||
using RequestCmdID = NetEase.RequestCmdID;
|
||
|
||
// using AntiCheatResult = NTESHTPSec.AntiCheatResult;
|
||
|
||
namespace YiDun.Protector
|
||
{
|
||
public abstract class YiDunManager
|
||
{
|
||
private static YiDunManager _instance;
|
||
private static readonly object Lock = new object();
|
||
|
||
public static YiDunManager Instance
|
||
{
|
||
get
|
||
{
|
||
lock (Lock)
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
// 创建具体实现类的实例
|
||
_instance = CreateConcreteInstance();
|
||
if (_instance != null)
|
||
{
|
||
Debug.Log($"[YiDunManager] : {_instance.GetType().Name}");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError("[YiDunManager] Failed!");
|
||
}
|
||
}
|
||
|
||
return _instance;
|
||
}
|
||
}
|
||
}
|
||
|
||
private static YiDunManager CreateConcreteInstance()
|
||
{
|
||
// 这里可以根据平台或其他条件返回不同的具体实现
|
||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||
return new YiDunAndroidManager();
|
||
#elif UNITY_IOS && !UNITY_EDITOR
|
||
return new YiDunIOSManager();
|
||
#else
|
||
return new DefaultYiDunManager();
|
||
#endif
|
||
}
|
||
|
||
//
|
||
private string ProductId { get; set; }
|
||
private string BusinessId { get; set; }
|
||
|
||
private string Token { get; set; }
|
||
private string Account { get; set; }
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 销毁单例实例(主要用于测试)
|
||
/// </summary>
|
||
public static void DestroyInstance()
|
||
{
|
||
lock (Lock)
|
||
{
|
||
if (_instance != null)
|
||
{
|
||
_instance.Cleanup();
|
||
_instance = null;
|
||
Debug.Log("[YiDunManager] 单例实例已销毁");
|
||
}
|
||
}
|
||
}
|
||
|
||
public void Cleanup()
|
||
{
|
||
}
|
||
|
||
// 标记
|
||
// public static string PlatformFlag = "";
|
||
|
||
public abstract string PlatformFlag { get; }
|
||
//
|
||
private HTProtectConfig _config;
|
||
|
||
public delegate void OutAction<T>(out T result);
|
||
|
||
public void Init(string productId, YiDunHtpCallback yiDunHtpCallback)
|
||
{
|
||
Debug.Log($"Init-> {PlatformFlag} {productId}");
|
||
ProductId = productId;
|
||
|
||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||
NetSecProtect.init(productId, yiDunHtpCallback, _config);
|
||
#elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||
// NTESRiskSecProtect.init(productId, (code,message,content ) =>
|
||
// {
|
||
// Debug.Log($"init callback -> {productId} {code} {message} {content}");
|
||
// // yiDunHtpCallback.onReceive(code,message);
|
||
// NTESRiskSecProtect.ioctl(NTESHTPSec.RequestCmdID.Cmd_SetConfigData,message);
|
||
// });
|
||
|
||
NTESRiskSecProtect.setServerType(_config.getServerType());
|
||
NTESRiskSecProtect.setChannel(_config.getChannel());
|
||
NTESRiskSecProtect.InitCallback initCallback = new NTESRiskSecProtect.InitCallback(InitProductIdCallback);
|
||
NTESRiskSecProtect.init(productId, initCallback);
|
||
#endif
|
||
|
||
|
||
}
|
||
|
||
[MonoPInvokeCallback(typeof(NTESRiskSecProtect.InitCallback))]
|
||
public static void InitProductIdCallback(int code, string message, string content)
|
||
{
|
||
Debug.Log($"init callback -> {code} {message} {content}");
|
||
// code返回200说明初始化成功
|
||
NTESRiskSecProtect.ioctl(NTESHTPSec.RequestCmdID.Cmd_SetConfigData,message);
|
||
|
||
|
||
}
|
||
//
|
||
public YiDunManager InitConfig(OutAction<HTProtectConfig> act)
|
||
{
|
||
act?.Invoke(out _config);
|
||
#if (UNITY_ANDROID) && !UNITY_EDITOR
|
||
Debug.Log($"InitConfig() Android");
|
||
#elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||
Debug.Log($"InitConfig() IOS");
|
||
NTESRiskSecProtect.setChannel(_config.getChannel());
|
||
#else
|
||
Debug.Log($"InitConfig() Default");
|
||
#endif
|
||
return this;
|
||
}
|
||
|
||
|
||
// 获取超时时间 3000秒
|
||
private int timeOut = 3000;
|
||
private int _tryTimesForGetToken = 0;
|
||
private int MaxTimesForGetToken = 3;
|
||
private bool IsTokenGetProcessed = false;
|
||
|
||
private TaskCompletionSource<bool> _taskCompletionForGetToken;
|
||
|
||
|
||
public async Task<bool> StartGetToken()
|
||
{
|
||
// Log("StartGetToken-> ");
|
||
_tryTimesForGetToken = 0;
|
||
_taskCompletionForGetToken = new TaskCompletionSource<bool>();
|
||
await TryGetToken();
|
||
return await _taskCompletionForGetToken.Task;
|
||
}
|
||
|
||
private async Task TryGetToken()
|
||
{
|
||
// Log("TryGetToken()-> ");
|
||
var task = new TaskCompletionSource<bool>();
|
||
GetTokenAsync(timeOut,BusinessId, new YiDunGetTokenCallback( (code, message) =>
|
||
{
|
||
if (code == AntiCheatResult.OK)
|
||
{
|
||
Debug.Log($"GetToken Succeed In {_tryTimesForGetToken} times");
|
||
Token = message;
|
||
FinishGetToken();
|
||
}
|
||
task.TrySetResult(true);
|
||
}));
|
||
await task.Task;
|
||
if (IsTokenGetProcessed)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_tryTimesForGetToken >= MaxTimesForGetToken)
|
||
{
|
||
return;
|
||
}
|
||
await TryNextGetToken();
|
||
}
|
||
|
||
private void FinishGetToken()
|
||
{
|
||
Log("FinishGetToken ");
|
||
IsTokenGetProcessed = true;
|
||
var bRet = _taskCompletionForGetToken.TrySetResult(true);
|
||
if (!bRet)
|
||
{
|
||
Log("FinishGetToken-> Failed");
|
||
}
|
||
}
|
||
|
||
private async Task TryNextGetToken()
|
||
{
|
||
_tryTimesForGetToken++;
|
||
Log($"TryNextGetToken For {_tryTimesForGetToken} times");
|
||
await TryGetToken();
|
||
}
|
||
//
|
||
public int SetRoleInfo(string businessId, string roleId, string roleName, string roleAccount, string roleServer,
|
||
int serverId, string gameJson)
|
||
{
|
||
BusinessId = businessId;
|
||
Account = roleAccount;
|
||
|
||
Log($"SetRoleInfo ->{businessId},{roleId} {roleName} {roleAccount} {roleServer} {serverId} {gameJson}");
|
||
#if (UNITY_ANDROID ) && !UNITY_EDITOR
|
||
return NetSecProtect.setRoleInfo(businessId, roleId, roleName, roleAccount, roleServer, serverId, gameJson);
|
||
#elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||
return NTESRiskSecProtect.setRoleInfo(businessId, roleId, roleName, roleAccount, roleServer, serverId, gameJson);
|
||
#endif
|
||
|
||
return 0;
|
||
}
|
||
|
||
//
|
||
public void LogOut()
|
||
{
|
||
#if (UNITY_ANDROID ) && !UNITY_EDITOR
|
||
NetSecProtect.logOut();
|
||
#elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||
NTESRiskSecProtect.logOut();
|
||
#endif
|
||
Debug.Log("LogOut");
|
||
}
|
||
|
||
//
|
||
public void GetTokenAsync(int timeOut, string businessId, YiDunGetTokenCallback callback)
|
||
{
|
||
#if (UNITY_ANDROID ) && !UNITY_EDITOR
|
||
NetSecProtect.getTokenAsync(timeOut, businessId, callback);
|
||
#elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||
// NTESRiskSecProtect.getTokenAsync(timeOut,businessId, (token, code, codeStr, businessId) =>
|
||
NTESRiskSecProtect.getTokenAsync(timeOut,businessId,(result) =>
|
||
{
|
||
callback.onResult(new AntiCheatResult(result.code,result.codeStr,result.token,businessId) );
|
||
});
|
||
#else
|
||
callback.onResult(new AntiCheatResult(200,"token","simulator_token",businessId));
|
||
#endif
|
||
Log($"GetTokenAsync-> {timeOut} {businessId}");
|
||
}
|
||
|
||
|
||
/**
|
||
*
|
||
* 拼接组装参与计算的params参数示例
|
||
* 不同接口校验参数不同,详见目录下的demo文件: https://github.com/yidun/irisk-openapi-demo/blob/main/irisk-openapi-csharp-demo
|
||
*/
|
||
private static Dictionary<string, string> GetParamsMap(RiskRequest riskRequest)
|
||
{
|
||
var parameters = new Dictionary<String, String>
|
||
{
|
||
// { "productId", riskRequest.productId },
|
||
{ "secretId", riskRequest.secretId },
|
||
{ "businessId", riskRequest.businessId },
|
||
{ "timestamp", riskRequest.timestamp },
|
||
{ "nonce", riskRequest.nonce },
|
||
{ "version", riskRequest.version },
|
||
{ "token", riskRequest.token },
|
||
{ "account", riskRequest.account },
|
||
{ "ip", riskRequest.ip },
|
||
{ "sceneData", riskRequest.sceneData }
|
||
};
|
||
return parameters;
|
||
}
|
||
|
||
// 获取签名
|
||
public static string GenSignature(String secretKey, Dictionary<String, String> parameters)
|
||
{
|
||
parameters = parameters.OrderBy(o => o.Key, StringComparer.Ordinal).ToDictionary(o => o.Key, p => p.Value);
|
||
var builder = new StringBuilder();
|
||
foreach (var kv in parameters)
|
||
{
|
||
builder.Append(kv.Key).Append(kv.Value);
|
||
}
|
||
|
||
builder.Append(secretKey);
|
||
var tmp = builder.ToString();
|
||
MD5 md5 = new MD5CryptoServiceProvider();
|
||
var result = md5.ComputeHash(Encoding.UTF8.GetBytes(tmp));
|
||
builder.Clear();
|
||
foreach (var b in result)
|
||
{
|
||
builder.Append(b.ToString("x2").ToLower());
|
||
}
|
||
|
||
return builder.ToString();
|
||
}
|
||
|
||
|
||
public YiDunManager BindTo(GameObject gameObject)
|
||
{
|
||
#if (UNITY_ANDROID && !UNITY_EDITOR)
|
||
if (!gameObject.GetComponent<NetSecProtect>())
|
||
{
|
||
gameObject.AddComponent<NetSecProtect>();
|
||
}
|
||
#elif (UNITY_IOS|| UNITY_IPHONE) && !UNITY_EDITOR
|
||
if (!gameObject.GetComponent<NTESRiskSecProtect>())
|
||
{
|
||
gameObject.AddComponent<NTESRiskSecProtect>();
|
||
}
|
||
#endif
|
||
return this;
|
||
}
|
||
|
||
|
||
private string CheckURL = "http://ir-open.dun.163.com/v6/risk/check";
|
||
// private string ContentType = "Content-Type:application/json";
|
||
private string SecretId = "7992abe1b4c13dddf09b6d5e6614f790";
|
||
private string SecretKey = "513123689ad874638be2ab02edd9c826";
|
||
|
||
|
||
public class RiskRequest
|
||
{
|
||
// public string productId;
|
||
public string businessId;
|
||
public string secretId;
|
||
public string timestamp;
|
||
|
||
//
|
||
public readonly string nonce = Guid.NewGuid().ToString("N");
|
||
public readonly string version = "603";
|
||
public string signature;
|
||
public string token;
|
||
public string account;
|
||
// public string roleId;
|
||
// public string nickname;
|
||
// public string phone;
|
||
// public string email;
|
||
// public long registerTime; // 注册事件,可不填
|
||
public readonly string ip = "unknown";
|
||
public string sceneData;
|
||
|
||
}
|
||
|
||
|
||
private const int TIME_DELTA = 60 * 2;
|
||
private DateTime _riskTime = DateTime.MinValue;
|
||
|
||
private bool CheckRiskEnable()
|
||
{
|
||
var timeSpan = DateTime.UtcNow - _riskTime;
|
||
// Log($"CheckRiskEnable() -> {timeSpan.TotalSeconds} > {TIME_DELTA} = {timeSpan.TotalSeconds > TIME_DELTA}");
|
||
return timeSpan.TotalSeconds > TIME_DELTA;
|
||
}
|
||
|
||
private async void DoRiskCheck(string sceneData)
|
||
{
|
||
if (!CheckRiskEnable() )
|
||
return ;
|
||
_riskTime = DateTime.UtcNow;
|
||
Log("DoRiskCheck-> ");
|
||
using (var client = new HttpClient())
|
||
{
|
||
try
|
||
{
|
||
client.Timeout = TimeSpan.FromSeconds(15);
|
||
var riskRequest = new RiskRequest()
|
||
{
|
||
// productId = ProductId,
|
||
businessId = BusinessId,
|
||
secretId = SecretId,
|
||
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(),
|
||
token = Token,
|
||
account = Account,
|
||
sceneData = sceneData
|
||
};
|
||
// var json = JsonConvert.SerializeObject(riskRequest);
|
||
|
||
riskRequest.signature = GenSignature(SecretKey, GetParamsMap(riskRequest));
|
||
|
||
var content = JsonConvert.SerializeObject(riskRequest);
|
||
var stringContent = new StringContent(content, Encoding.UTF8, "application/json");
|
||
var response = await client.PostAsync(CheckURL, stringContent);
|
||
var result = await response.Content.ReadAsStringAsync();
|
||
if (result != null)
|
||
{
|
||
var ret = JObject.Parse(result);
|
||
var code = ret.GetValue("code")?.ToObject<int>();
|
||
var msg = ret.GetValue("msg")?.ToObject<string>();
|
||
if (code == 200)
|
||
{
|
||
Debug.Log($"SUCCESS: code={code}, msg={msg}");
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"ERROR: code={code}, msg={msg}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// ignored
|
||
Debug.LogError(ex.Message + "\n" + ex.StackTrace);
|
||
}
|
||
finally
|
||
{
|
||
Token = string.Empty;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 定时器,定时发送RiskCheck
|
||
public async void PostRiskCheck(string sceneData = "{ }")
|
||
{
|
||
Log("PostRiskCheck()-> ");
|
||
if (string.IsNullOrEmpty(Token))
|
||
{
|
||
Log("token is Invalid - trying Get");
|
||
await StartGetToken();
|
||
if (string.IsNullOrEmpty(Token))
|
||
return;
|
||
}
|
||
DoRiskCheck(sceneData);
|
||
}
|
||
|
||
|
||
private static void Log(string message)
|
||
{
|
||
Debug.Log($"<color=cyan> YiDunManager => {message} </color>");
|
||
}
|
||
}
|
||
|
||
public class YiDunIOSManager : YiDunManager
|
||
{
|
||
public override string PlatformFlag => "iOS";
|
||
}
|
||
|
||
// 方便测试
|
||
public class DefaultYiDunManager : YiDunManager
|
||
{
|
||
public override string PlatformFlag => "defaut";
|
||
}
|
||
|
||
public class YiDunAndroidManager : YiDunManager
|
||
{
|
||
public override string PlatformFlag => "Android";
|
||
}
|
||
} |