首先要导入3个类型转换的扩展类文件
代码如下:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
namespace EH.Extend
{
/// <summary>
/// byte[] 扩展
/// </summary>
public static class EBytes
{
/// <summary>
/// 二进制数组转为16进制字符串
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string ToHex(this byte[] bytes)
{
if (bytes == null)
{
return "";
}
var sb = new StringBuilder();
foreach (var b in bytes)
{
sb.Append(b.ToString("X2"));
}
return sb.ToString();
}
/// <summary>
/// 字节数组数组转布尔数组
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static bool[] ToBits(this byte[] bytes)
{
var bits = new bool[bytes.Length * 8];
for (var i = 0; i < bytes.Length; i++)
{
var bools = bytes[i].ToBits();
Array.Copy(bools, 0, bits, i * 8, 8);
}
return bits;
}
/// <summary>
/// 字节数组转UTF8字符串
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string ToUTF8(this byte[] bytes)
{
return Encoding.UTF8.GetString(bytes);
}
/// <summary>
/// 字节数组转UTF8字符串
/// </summary>
/// <param name="bytes"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string ToUTF8(this byte[] bytes, int offset, int length)
{
return Encoding.UTF8.GetString(bytes, offset, length);
}
/// <summary>
/// 字节数组转UTF8字符串,再通过JsonConvert转泛型对象实例
/// </summary>
/// <param name="bytes"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T To<T>(this byte[] bytes)
{
var str = bytes.ToUTF8();
try
{
return JsonConvert.DeserializeObject<T>(str);
}
catch (Exception e)
{
return default(T);
}
}
/// <summary>
/// 字节数组转UTF8字符串,再通过JsonConvert转泛型对象实例
/// </summary>
/// <param name="bytes"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T To<T>(this byte[] bytes, int offset, int length)
{
var str = bytes.ToUTF8(offset, length);
try
{
return JsonConvert.DeserializeObject<T>(str);
}
catch (Exception e)
{
return default;
}
}
/// <summary>
/// 字节数组转UTF8字符串,再通过JsonConvert转类型实例
/// </summary>
/// <param name="bytes"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object To(this byte[] bytes, int offset, int length, Type type)
{
var str = bytes.ToUTF8(offset, length);
try
{
return JsonConvert.DeserializeObject(str, type);
}
catch (Exception e)
{
return null;
}
}
/// <summary>
/// 字节数组小尾读取指定长度的整型
/// </summary>
/// <param name="bytes"></param>
/// <param name="length"></param>
/// <returns></returns>
public static int ReadIntLE(this byte[] bytes, int length)
{
var count = 0;
var val = 0;
for (var i = length - 1; i > -1; i--)
{
val |= bytes[i] << (8 * count++);
}
return val;
}
/// <summary>
/// 字节数组小尾写入指定长度的整型
/// </summary>
/// <param name="bytes"></param>
/// <param name="val"></param>
/// <param name="length"></param>
public static void WriteIntLE(this byte[] bytes, int val, int length)
{
var count = 0;
for (var i = length - 1; i > -1; i--)
{
bytes[i] = (byte) (val >> (8 * count++));
}
}
/// <summary>
/// 字节数组Aes加密
/// </summary>
/// <param name="bytes"></param>
/// <param name="Key"></param>
/// <param name="iv"></param>
/// <returns></returns>
public static byte[] AesEncrypt(this byte[] bytes, byte[] Key, byte[] iv)
{
byte[] encrypted;
var len = Key.Length;
if (len < 32)
{
var k = new byte[32];
Array.Copy(Key, k, len);
Key = k;
}
len = iv.Length;
if (len < 16)
{
var i = new byte[16];
Array.Copy(iv, i, len);
iv = i;
}
using (var aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = iv;
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var bwEncrypt = new BinaryWriter(csEncrypt))
{
bwEncrypt.Write(bytes);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
/// <summary>
/// Aes加密
/// </summary>
/// <param name="bytes"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
public static byte[] AesEncrypt(this byte[] bytes, string key, string iv)
{
return bytes.AesEncrypt(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(iv));
}
/// <summary>
/// Aes解密
/// </summary>
/// <param name="cipherBytes"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
public static byte[] AesDecrypt(this byte[] cipherBytes, byte[] key, byte[] iv)
{
var len = key.Length;
if (len < 32)
{
var k = new byte[32];
Array.Copy(key, k, len);
key = k;
}
len = iv.Length;
if (len < 16)
{
var i = new byte[16];
Array.Copy(iv, i, len);
iv = i;
}
byte[] bytes;
using (var aesAlg = Aes.Create())
{
aesAlg.Key = key;
aesAlg.IV = iv;
var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (var msDecrypt = new MemoryStream(cipherBytes))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (var brDecrypt = new BinaryReader(csDecrypt))
{
var l = (int) msDecrypt.Length;
bytes = brDecrypt.ReadBytes(l);
}
}
}
}
return bytes;
}
/// <summary>
/// Aes解密
/// </summary>
/// <param name="cipherBytes"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
public static byte[] AesDecrypt(this byte[] cipherBytes, string key, string iv)
{
return cipherBytes.AesDecrypt(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(iv));
}
}
}
using Newtonsoft.Json;
namespace EH.Extend
{
public static class EObject
{
/// <summary>
/// 对象转Json字符串
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToJson(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
/// <summary>
/// 对象转Json字符串,忽略循环
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToJsonIgnoreLoop(this object obj)
{
var setting = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
return JsonConvert.SerializeObject(obj, setting);
}
/// <summary>
/// 对象转字节数组
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static byte[] ToJsonBytes(this object obj)
{
return JsonConvert.SerializeObject(obj).ToBytes();
}
}
}
using System;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace EH.Extend
{
public static class EString
{
/// <summary>
/// 按指定长度填补字符
/// </summary>
/// <param name="str"></param>
/// <param name="fill"></param>
/// <param name="len"></param>
/// <param name="pre"></param>
/// <returns></returns>
public static string FillStr(this string str, string fill = "0", int len = 2, bool pre = true)
{
while (str.Length < len)
{
if (pre)
{
str = fill + str;
}
else
{
str += fill;
}
}
return str;
}
/// <summary>
/// Json字符串反序列化对象
/// </summary>
/// <param name="str"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T To<T>(this string str)
{
try
{
return JsonConvert.DeserializeObject<T>(str);
}
catch (Exception e)
{
return default(T);
}
}
/// <summary>
/// 字符串转JObject
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static JObject ToJObj(this string str)
{
try
{
return JsonConvert.DeserializeObject<JObject>(str);
}
catch (Exception e)
{
return null;
}
}
/// <summary>
/// 十六进制字符串转字节数组
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
public static byte[] HexToBytes(this string hex)
{
hex = hex.Replace(" ", "");
if (hex.Length % 2 != 0)
{
hex += " ";
}
var len = hex.Length / 2;
var bytes = new byte[len];
for (var i = 0; i < len; i++)
{
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
}
return bytes;
}
/// <summary>
/// UTF8字符串转二进制数组
/// </summary>
/// <param name="utf8"></param>
/// <returns></returns>
public static byte[] ToBytes(this string utf8)
{
return Encoding.UTF8.GetBytes(utf8);
}
/// <summary>
/// 删除文件
/// </summary>
/// <param name="path"></param>
public static void Del(this string path)
{
if (File.Exists(path))
{
File.Delete(path);
}
}
/// <summary>
/// 文件是否存在
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static bool Exists(this string path)
{
return File.Exists(path);
}
/// <summary>
/// 保存二进制文件
/// </summary>
/// <param name="path"></param>
/// <param name="bytes"></param>
/// <param name="mode"></param>
public static void Save(this string path, byte[] bytes, FileMode mode = FileMode.CreateNew)
{
var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
using (var fs = File.Open(path, mode))
{
fs.Write(bytes, 0, bytes.Length);
}
}
/// <summary>
/// UTF8字符串保存文件
/// </summary>
/// <param name="path"></param>
/// <param name="str"></param>
/// <param name="mode"></param>
public static void Save(this string path, string str, FileMode mode = FileMode.CreateNew)
{
path.Save(Encoding.UTF8.GetBytes(str), mode);
}
/// <summary>
/// 按路径加载二进制文件
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static byte[] Load(this string path)
{
var file = new FileInfo(path);
if (!file.Exists) return null;
using (var fs = File.OpenRead(path))
{
var bytes = new byte[fs.Length];
fs.Read(bytes, 0, bytes.Length);
return bytes;
}
}
/// <summary>
/// 遍历路径下的文件
/// </summary>
/// <param name="path"></param>
/// <param name="filter"></param>
public static void FilterFiles(this string path, Action<FileInfo> fileFilter, string searchPattern = null, Func<DirectoryInfo, bool> dirFilter = null)
{
var dir = new DirectoryInfo(path);
if (!dir.Exists) return;
var files = dir.GetFiles(searchPattern);
foreach (var fileInfo in files)
{
fileFilter(fileInfo);
}
var dirs = dir.GetDirectories();
foreach (var d in dirs)
{
if (dirFilter == null || dirFilter(d))
{
FilterFiles(d.FullName, fileFilter, searchPattern, dirFilter);
}
}
}
/// <summary>
/// 字符串转枚举
/// </summary>
/// <param name="str"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T ToEnum<T>(this string str)
{
return (T) Enum.Parse(typeof(T), str);
}
// /// <summary>
// /// 编辑器下阻塞运行exe
// /// </summary>
// /// <param name="path"></param>
// /// <param name="argument"></param>
// /// <param name="workDir"></param>
// /// <param name="wait"></param>
// public static void ShellProcessCommand(this string path, string argument, string workDir = null,
// bool wait = false)
// {
#if !UNITY_EDITOR_WIN
throw new NotSupportedException();
#endif
// try
// {
// var start = new ProcessStartInfo(path)
// {
// Arguments = argument,
// WorkingDirectory = workDir,
// };
//
// var process = Process.Start(start);
// if (!wait) return;
// process.WaitForExit();
// process.Close();
// }
// catch (Exception ex)
// {
// Debug.Log("出错原因:" + ex.Message);
// }
// }
//
// /// <summary>
// /// 运行时异步调用exe
// /// </summary>
// /// <param name="path"></param>
// /// <param name="argument"></param>
// /// <param name="onComplete"></param>
// /// <param name="onError"></param>
// /// <param name="workDir"></param>
// /// <param name="createNoWindow"></param>
// /// <param name="errorDialog"></param>
// public static void ProcessCommand(this string path, string argument, Action<string> onComplete = null,
// Action<IError> onError = null, string workDir = null)
// {
// if (RuntimePlatform.WindowsEditor != Application.platform &&
// RuntimePlatform.WindowsPlayer != Application.platform) return;
//
// FW.Thread.Push(() =>
// {
// var arr = new string[2];
// Process process = null;
// try
// {
// var start = new ProcessStartInfo(path)
// {
// Arguments = argument,
// CreateNoWindow = true,
// UseShellExecute = false,
// WorkingDirectory = workDir,
// RedirectStandardOutput = true,
// RedirectStandardError = true,
// RedirectStandardInput = true,
// StandardOutputEncoding = Encoding.UTF8,
// StandardErrorEncoding = Encoding.UTF8,
// };
//
// process = Process.Start(start);
// arr[0] = process.StandardOutput.ReadToEnd();
// arr[1] = process.StandardError.ReadToEnd();
// process.Close();
// return arr;
// }
// catch (Exception ex)
// {
// arr[1] = ex.Message;
// }
// finally
// {
// process?.Close();
// }
//
// return arr;
// }, arr =>
// {
// if (!string.IsNullOrEmpty(arr[1]))
// {
// onError?.Invoke(new Error(arr[1]));
// }
//
// onComplete?.Invoke(arr[0]);
// });
// }
}
}
然后
using System;
using System.Collections;
using System.IO;
using EH.Extend;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Networking;
namespace LabsHuanKong
{
/// <summary>
///授权系统
///版本0.1
///注意设置项目的公司名和项目名
/// </summary>
public class SAuth:MonoBehaviour
{
public event Action<Auth.EAuth> OnVerify;
public string company { get; private set; }
public string product { get; private set; }
private string authCodePath;
private string timeCodePath;
private Auth.AuthObj auth;
public void Init()
{
authCodePath = Path.Combine(Application.persistentDataPath, "auth", "sn.txt");
timeCodePath = Path.Combine(Application.persistentDataPath, "auth", "tm.txt");
DontDestroyOnLoad(this);
company = Application.companyName;
if (string.IsNullOrEmpty(company))
{
Debug.Log("not set company name");
}
product = Application.productName;
if (string.IsNullOrEmpty(product))
{
Debug.Log("not set product name");
}
InvokeRepeating("VerifyLocal", 0, 60);
}
private IEnumerator GetNetTime()
{
using( var req = UnityWebRequest.Get("https://www.baidu.com"))
{
yield return req.SendWebRequest();
if (!req.isDone)
{
yield break;
}
var date = req.GetResponseHeader("Date");
if (!DateTime.TryParse(date, out var netDt))
{
yield break;
}
var span = netDt - DateTime.Now;
if (span.TotalHours < 2)
{
yield break;
}
OnVerify?.Invoke(Auth.EAuth.LocalTimeError);
}
}
/// <summary>
/// 验证本地授权文件
/// </summary>
public void VerifyLocal()
{
StartCoroutine( GetNetTime());
if (!File.Exists(authCodePath) || !File.Exists(timeCodePath))
{
OnVerify?.Invoke(Auth.EAuth.Unauthorized);
return;
}
var timeCode = timeCodePath.Load().ToUTF8();
var obj = Auth.DecryptCode(timeCode, company, product);
if (obj == null)
{
OnVerify?.Invoke(Auth.EAuth.Unauthorized);
return;
}
var start = obj.Value<string>("start");
var last = obj.Value<string>("last");
var end = obj.Value<string>("end");
var st = new DateTime();
if (!DateTime.TryParse(start, out st))
{
OnVerify?.Invoke(Auth.EAuth.Invalid);
return;
}
var lt = new DateTime();
if (!DateTime.TryParse(last, out lt))
{
OnVerify?.Invoke(Auth.EAuth.Invalid);
return;
}
var et = new DateTime();
if (!DateTime.TryParse(end, out et))
{
OnVerify?.Invoke(Auth.EAuth.Invalid);
return;
}
if (st > lt || lt > et)
{
timeCodePath.Del();
OnVerify?.Invoke(Auth.EAuth.Invalid);
return;
}
var authCode = authCodePath.Load().ToUTF8();
auth = Auth.Verify(authCode, company, product);
if (!auth.start.Equals(st) || !auth.end.Equals(et))
{
OnVerify?.Invoke(Auth.EAuth.Invalid);
return;
}
if (auth.auth == Auth.EAuth.Success)
{
SaveTimeObj();
}
OnVerify?.Invoke(auth.auth);
}
/// <summary>
/// 保存授权文件,并保存时间数据
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public Auth.EAuth SaveAuthCode(string code)
{
var ao = Auth.Verify(code, company, product);
if (ao.auth != Auth.EAuth.Success)
{
return Auth.EAuth.Invalid;
}
if (DateTime.Now < ao.start)
{
return Auth.EAuth.Invalid;
}
authCodePath.Del();
authCodePath.Save(code.ToBytes());
auth = ao;
SaveTimeObj();
return Auth.EAuth.Success;
}
/// <summary>
/// 保存时间数据,注意如果没有正确的时间信息也被判断授权失效
/// </summary>
private void SaveTimeObj()
{
var obj = new JObject
{
{"start", auth.start.ToShortDateString()},
{"last", DateTime.Now.ToShortDateString()},
{"end", auth.end.ToShortDateString()}
};
timeCodePath.Del();
timeCodePath.Save(Auth.EncryptCode(obj, company, product));
}
/// <summary>
/// 获取请求码
/// </summary>
/// <returns></returns>
public string GetRequestCode()
{
return Auth.Request(company, product);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace LabsHuanKong
{
public class AuthForm : UGuiForm
{
[SerializeField]
private TMP_InputField _authCodeIpt;
[SerializeField]
private TMP_InputField _verifyCodeIpt;
[SerializeField]
private Button _verifyBtn;
[SerializeField]
private TextMeshProUGUI _verifyTip;
private static Action _onVerifySuccess;
// 需提前注册SAuth系统
public void VerifyLocal(Action action = null)
{
_onVerifySuccess = action;
ClientManager.Instance.GetSAuthSystem().OnVerify += OnVerify;
ClientManager.Instance.GetSAuthSystem().VerifyLocal();
}
private void OnVerify(Auth.EAuth type)
{
switch (type)
{
case Auth.EAuth.Invalid:
case Auth.EAuth.Unauthorized:
case Auth.EAuth.Expire:
case Auth.EAuth.LocalTimeError:
ShowTip(type);
GameEntry.UI.OpenUIForm(UIFormId.AuthForm, this);
break;
case Auth.EAuth.Success:
GameEntry.UI.GetUIForm(UIFormId.AuthForm).Close();
_onVerifySuccess?.Invoke();
break;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
ClientManager.Instance.GetSAuthSystem().OnVerify -= OnVerify;
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
_authCodeIpt.text = ClientManager.Instance.GetSAuthSystem().GetRequestCode();
// 取消注释,自动生成授权码
//_verifyCodeIpt.text = Auth.Calculate("keda", Application.companyName, Application.productName, DateTime.Parse("2021-1-1"), _authCodeIpt.text);
_verifyBtn.onClick.AddListener(OnVerifyClick);
}
protected override void OnClose(object userData)
{
base.OnClose(userData);
_verifyBtn.onClick.RemoveAllListeners();
}
private void OnVerifyClick()
{
var verifyCode = _verifyCodeIpt.text;
if (string.IsNullOrEmpty(verifyCode))
{
_verifyTip.text = "请输入授权码";
_verifyTip.color = Color.red;
return;
}
var obj = ClientManager.Instance.GetSAuthSystem().SaveAuthCode(_verifyCodeIpt.text);
_verifyTip.text = GetTip(obj);
if (obj == Auth.EAuth.Success)
{
GameEntry.UI.GetUIForm(UIFormId.AuthForm).Close();
_onVerifySuccess?.Invoke();
}
}
private string GetTip(Auth.EAuth obj)
{
switch (obj)
{
case Auth.EAuth.Expire:
_verifyTip.color = Color.red;
return "授权码已过期";
case Auth.EAuth.Invalid:
_verifyTip.color = Color.red;
return "无效的授权码";
case Auth.EAuth.Unauthorized:
_verifyTip.color = Color.red;
return "未授权";
case Auth.EAuth.LocalTimeError:
_verifyTip.color = Color.red;
return "本机时间异常";
case Auth.EAuth.Success:
_verifyTip.color = Color.yellow;
return "授权成功";
default:
throw new ArgumentOutOfRangeException();
}
}
public void ShowTip(Auth.EAuth obj)
{
_verifyTip.text = GetTip(obj);
}
}
}