官方网站下载qq最新版,文化传媒公司广告宣传,章丘建设局网站,中山网站运营Unity开发授权系统
引子
因为有些客户尾款到账不及时#xff0c;因此研究了一套授权系统#xff0c;当授权到期后#xff0c;系统就提示软件授权已到期#xff0c;不能继续使用云云#xff0c;这样方便尾款的收回。
大体需求就是
时间相关性#xff0c;可以自由设置授…Unity开发授权系统
引子
因为有些客户尾款到账不及时因此研究了一套授权系统当授权到期后系统就提示软件授权已到期不能继续使用云云这样方便尾款的收回。
大体需求就是
时间相关性可以自由设置授权到期日期到达时间后提示过期。机器相关性有时候是按照机器来授权的需要识别不同的机器分别给客户授权。
思路和需要的技术点
提取机器特征码可以参考的思路是CPU序列号、硬盘序列号、主板序列号、网卡MAC地址等。其中获取MAC地址最简单但是也最不稳定客户网络环境如果比较复杂比如多张网卡比如拔插网线可能就会导致网卡状态变化读取时就会有问题。其余的相对好一点推荐几个序列号都读取一下然后拼接使用。但也不是百分百稳定毕竟各种厂商设备型号繁杂。问题的关键在于如何获取这些序列号网络上搜寻的解决方案全都是使用System.Management然而这个其实并不能直接在unity中使用。即便是将Unity InstallPath/Editor/Data/MonoBleedingEdge/lib/mono/2.0-api/System.Management.dll文件引入Unity工程也会报“未实现”的错。所以最终我的解决方案是写了一个额外的控制台程序在这个控制台程序中读取硬件信息然后输出在Unity中调用这个控制台程序在后台去获取硬件信息。这就又引发了一系列其他的问题比如假设客户知道了原理如果他伪造一个读取硬件信息的程序当我unity去调用时他就可以返回给我假的硬件特征码比如无论什么机器都返回一个固定的值这样他就可以只用一台机器的授权好几台机器共用了。所以我Unity在调用之前必须判断好这个读取硬件特征码的软件是我的软件而不是他伪造的。还有一个问题在unity中调用的时候必须是后台调用不能出现任何界面。 那么两个问题的解决方法就是 在调用外部进程之前首先创建一个内存映射文件MemoryMappedFile它虽然叫做File但其实它只需要在内存中实际并不需要磁盘IO这个内存映射文件是可以跨进程共享数据的。读硬件特征码的程序读到硬件信息后加密后写到这个内容映射文件中而不是在控制台输出这样这个读取硬件特征的软件客户就很难替换因为①客户不知道内存映射文件对象的名称②客户不知道加密方式或者说即便知道加密方式不知道加密的key无法向内存映射文件中写如正确数值。这样就避免了客户自行替换读硬件信息的程序。 提取特征码的具体算法网络上很多这里不再赘述。在unity中静静的运行一个后台进程其实很容易这里以获取控制台输出为例
// 读取机器码的协程
private static IEnumerator ReadMachineCode()
{// 启动读取硬件特征码的进程string exePath Path.Combine(Application.streamingAssetsPath, MachineCode/GetMachineCode.exe);if (!File.Exists(exePath)){OnMachineCodeReaded?.Invoke(null);yield break;}Process process new Process();process.StartInfo.FileName exePath;process.StartInfo.Arguments Your Some Arguments; // 据实际情况写process.StartInfo.UseShellExecute false;process.StartInfo.CreateNoWindow true;process.StartInfo.WindowStyle ProcessWindowStyle.Hidden;process.StartInfo.WorkingDirectory Path.Combine(Application.streamingAssetsPath, MachineCode);process.StartInfo.RedirectStandardOutput true;process.Start();// 等待进程结束while (!process.HasExited)yield return null;// 获取进程运行结果以控制台为例而不是内存文件映射生产环境应读取内存文件映射中的结果string result process.StandardOutput.ReadToEnd();OnMachineCodeReaded?.Invoke(result);
}构建授权数据本质上是构架一个数据体用于描述授权文件的信息。然后转换为Json字符串方便加密和存储。
[Serializable]
internal class MachineLicense
{public string code; // 目标机器码public string desc; // 说明public int year; // 授权到期年public int month; // 授权到期月public int day; // 授权到期日
}
[Serializable]
internal class LicenseData
{public string projectName; // 项目名称public string description; // 说明public string check; // 校验字符串public ListMachineLicense data; // 授权数据
}AES加密原理采用AES加密。这也是很成熟的加密算法下面提供一些与加解密相关的封装好的方法
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;public static class AESCryptography
{// AES加密public static byte[] Encrypt(byte[] rgbKey, byte[] rgbIV, string sourceText){using MemoryStream memoryStream new MemoryStream();using (Aes aes Aes.Create())using (ICryptoTransform transform aes.CreateEncryptor(rgbKey, rgbIV))using (CryptoStream cryptoStream new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))using (StreamWriter streamWriter new StreamWriter(cryptoStream)){streamWriter.Write(sourceText);streamWriter.Flush();}return memoryStream.ToArray();}// AES解密public static string Decrypt(byte[] rgbKey, byte[] rgbIV, byte[] cipherBuffer){using MemoryStream stream new MemoryStream(cipherBuffer);using Aes aes Aes.Create();using ICryptoTransform transform aes.CreateDecryptor(rgbKey, rgbIV);using CryptoStream cryptoStream new CryptoStream(stream, transform, CryptoStreamMode.Read);using StreamReader streamReader new StreamReader(cryptoStream);return streamReader.ReadToEnd();}// 字符串MD5加密public static byte[] Md5Encrypt(string tex){var md5 MD5.Create();return md5.ComputeHash(Encoding.UTF8.GetBytes(tex));}// 将byte[]数据输出为HEX字符串public static string ByteArrayToString(byte [] data){StringBuilder sb new StringBuilder();int index 0;foreach( var d in data ){if (index 0 index % 4 0)sb.Append(-);sb.Append(d.ToString(X2));index;}return sb.ToString();}// 将HEX字符串转换为byte[16]public static bool HexStringToByte16(string hex, out byte[] data){data new byte[16];int index 0;bool half true;foreach (var ch in hex){byte d;switch (ch){case 0 and 9:d (byte)(ch - 0);break;case A and F:d (byte)(10 (ch - A));break;case a and f:d (byte)(10 (ch - a));break;case :case -:case ::continue;default:return false;}if (half)data[index] (byte) (d 4);else{data[index] (byte) ( data[index] | d );index;if (index 15)break;}half !half;}return true;}
}制作授权文件Unity端判定授权无效或授权到期后系统会停止工作并将机器码显示到屏幕上提示授权到期信息等。我方技术人员获取到机器码后按照上述算法制作授权文件经过AES加密生成Base64字符串保存到授权文件中交付客户。授权验证客户将授权文件存放到指定路径后Unity端验证过程首先读取授权文件并进行解密转换为解密后的json字符串再转换为LicneseData数据如果校验码等信息全部通过后再判定当前设备机器码是否在授权文件中并且授权日期是否到期。完成授权验证。
foreach (var date in from target in licenseData.data where string.CompareOrdinal(target.code, MachineCode) 0 select new DateTime(target.year, target.month, target.day).AddDays(1))
{OnLicenseChecked?.Invoke(DateTime.Now date);yield break;
}
OnLicenseChecked?.Invoke(false);