如何加密含多DeepLinkDataDto的深度链接且不超URL字符限制?
问题描述
当尝试对包含20个以上DeepLinkDataDto的DeepLinkModel进行加密时,生成的加密字符串过长,超出URL字符上限,导致浏览器打开深度链接时报错:
"The resource you are looking for has been removed, had its name changed, or is temporarily unavailable."
当前加密逻辑代码:
var deepLinkQueryString = DeepLinkDataProtectionProvider .EncryptString(deepLinkModel.AsJson(), deepLinkModelEncryptionKey)
现有加密解密类:
public static class DeepLinkDataProtectionProvider { private static readonly byte[] InitializationVector = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; /// <summary> /// Encrypt invoice deep link query string /// </summary> /// <param name="text">string text</param> /// <param name="key">string key</param> /// <returns>Encrypted string</returns> public static string EncryptString(string text, string key) { byte[] array; // Create a new instance of the Aes // class. This generates a new key and initialization using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); // Create an encryptor to perform the stream transform. ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, InitializationVector); // Create the streams used for encryption. using (MemoryStream memoryStream = new()) { using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write)) { using (StreamWriter streamWriter = new(cryptoStream)) { //Write all data to the stream. streamWriter.Write(text); } array = memoryStream.ToArray(); } } } // Return the encrypted bytes from the memory stream. return Convert.ToBase64String(array); } /// <summary> /// Decrypt invoice deep link query string /// </summary> /// <param name="cipherText">string cipherText</param> /// <param name="key">string key</param> /// <returns>Decrypted string</returns> public static string DecryptString(string cipherText, string key) { byte[] buffer = Convert.FromBase64String(cipherText.Replace(" ", "+")); // Create an Aes object // with the specified key and IV. using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); // Create a decryptor to perform the stream transform. ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, InitializationVector); // Create the streams used for decryption. using (MemoryStream memoryStream = new(buffer)) { using (CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read)) { using (StreamReader streamReader = new(cryptoStream)) { // Read the decrypted bytes from the decrypting stream // and place them in a string. return streamReader.ReadToEnd(); } } } } } }
待加密模型类:
public class DeepLinkModel { [JsonProperty("RequestId")] public string RequestId { get; set; } [JsonProperty("UserId")] public string UserId { get; set; } [JsonProperty("Code")] public int Code { get; set; } [JsonProperty("Data")] public IEnumerable<DeepLinkDataDto> Data { get; set; } } public class DeepLinkDataDto { [JsonProperty("BillNumber")] public string BillNumber { get; set; } [JsonProperty("InvoiceNumber")] public string InvoiceNumber { get; set; } [JsonProperty("BillId")] public int BillId { get; set; } }
解决方案
1. 压缩后再加密
在加密前对JSON字符串进行GZIP压缩,大幅减少原始数据体积,再对压缩后的字节流加密,而非直接加密JSON字符串。
修改EncryptString方法:
public static string EncryptString(string text, string key) { byte[] array; using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, InitializationVector); using (MemoryStream memoryStream = new()) { // 先压缩JSON文本 using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, leaveOpen: true)) using (StreamWriter streamWriter = new(gzipStream)) { streamWriter.Write(text); } // 对压缩后的字节执行加密 memoryStream.Position = 0; using (CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Read)) using (MemoryStream encryptedStream = new()) { cryptoStream.CopyTo(encryptedStream); array = encryptedStream.ToArray(); } } } // 改用URL安全的Base64Url编码,避免转义增加长度 return Convert.ToBase64String(array).Replace('+', '-').Replace('/', '_').Replace("=", ""); }
对应修改DecryptString方法,先解密再解压:
public static string DecryptString(string cipherText, string key) { // 还原Base64Url为标准Base64格式 cipherText = cipherText.Replace('-', '+').Replace('_', '/'); switch (cipherText.Length % 4) { case 2: cipherText += "=="; break; case 3: cipherText += "="; break; } byte[] buffer = Convert.FromBase64String(cipherText); string result; using (Aes aes = Aes.Create()) { aes.Key = Encoding.UTF8.GetBytes(key); ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, InitializationVector); using (MemoryStream memoryStream = new(buffer)) using (CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read)) // 解密后解压还原原始JSON using (GZipStream gzipStream = new(cryptoStream, CompressionMode.Decompress)) using (StreamReader streamReader = new(gzipStream)) { result = streamReader.ReadToEnd(); } } return result; }
2. 使用Base64Url编码替代标准Base64
标准Base64的+、/、=字符在URL中需要URL转义,会额外增加字符串长度。改用Base64Url编码(替换+为-,/为_,去掉=填充),既符合URL安全要求,又减少最终字符数。
3. 优化JSON序列化体积
序列化DeepLinkModel时,使用紧凑配置减少JSON体积:
// 配置序列化选项,生成无空格的紧凑JSON var jsonSettings = new JsonSerializerSettings { Formatting = Formatting.None, // 可选:使用短属性名进一步压缩,比如将"RequestId"改为"rid",需同步修改模型JsonProperty特性 // ContractResolver = new CustomShortNameContractResolver() }; var compactJson = JsonConvert.SerializeObject(deepLinkModel, jsonSettings); var deepLinkQueryString = DeepLinkDataProtectionProvider.EncryptString(compactJson, deepLinkModelEncryptionKey);
4. 备选方案:后端存储+短标识
如果数据量极大,压缩加密后仍超出URL限制,可将完整数据存储到后端缓存/数据库,用短ID作为链接参数:
// 生成唯一标识并存储数据 var dataId = Guid.NewGuid().ToString(); // 设置合理过期时间,避免缓存堆积 Cache.Set(dataId, deepLinkModel, TimeSpan.FromHours(24)); // 生成深度链接时使用短ID var deepLinkQueryString = $"dataId={dataId}"; // 后端处理链接时还原数据 var deepLinkModel = Cache.Get<DeepLinkModel>(dataId); if (deepLinkModel == null) { // 处理数据过期或不存在的情况 }
内容的提问来源于stack exchange,提问作者Rakesh Kumar
相关产品推荐
相关产品推荐

