using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using WIDESEA_Core.Helper;
|
using Newtonsoft.Json.Serialization;
|
|
namespace WIDESEA_External.PLSService
|
{
|
public class MD5Util
|
{
|
// 获得32位的MD5加密
|
|
public static string GetMD5_32(string input)
|
{
|
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
|
byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
|
StringBuilder sb = new StringBuilder();
|
for (int i = 0; i < data.Length; i++)
|
{
|
sb.Append(data[i].ToString("x2"));
|
}
|
return sb.ToString();
|
}
|
|
|
// 获得16位的MD5加密
|
|
public static string GetMD5_16(string input)
|
{
|
return GetMD5_32(input).Substring(8, 16);
|
}
|
|
|
// 获得8位的MD5加密
|
|
public static string GetMD5_8(string input)
|
{
|
return GetMD5_32(input).Substring(8, 8);
|
}
|
|
|
// 获得4位的MD5加密
|
|
public static string GetMD5_4(string input)
|
{
|
return GetMD5_32(input).Substring(8, 4);
|
}
|
|
|
// 添加MD5的前缀,便于检查有无篡改
|
|
public static string AddMD5Profix(string input)
|
{
|
return GetMD5_4(input) + input;
|
}
|
|
|
// 移除MD5的前缀
|
|
public static string RemoveMD5Profix(string input)
|
{
|
return input.Substring(4);
|
}
|
|
|
// 验证MD5前缀处理的字符串有无被篡改
|
public static bool ValidateValue(string input)
|
{
|
bool res = false;
|
if (input.Length >= 4)
|
{
|
string tmp = input.Substring(4);
|
if (input.Substring(0, 4) == GetMD5_4(tmp))
|
{
|
res = true;
|
}
|
}
|
return res;
|
}
|
|
|
|
/// <summary>
|
/// 对json得键进行排序
|
/// </summary>
|
/// <param name="json"></param>
|
/// <returns></returns>
|
public static string SortJson(string json)
|
{
|
var tokenType = GetJsonType(json);
|
if (tokenType == JTokenType.Object)
|
{
|
var dic = JsonConvert.DeserializeObject<SortedDictionary<string, object>>(json);
|
SortedDictionary<string, object> keyValues = new SortedDictionary<string, object>(dic);
|
keyValues.OrderBy(m => m.Key);//升序 把Key换成Value 就是对Value进行排序
|
//keyValues.OrderByDescending(m => m.Key);//降序
|
SortedDictionary<string, object> tempKeyValues = new SortedDictionary<string, object>(keyValues);
|
foreach (KeyValuePair<string, object> kv in tempKeyValues)
|
{
|
// 判断value是不是JObject类型
|
Type t1 = kv.Value.GetType();
|
if (t1 == typeof(JObject))
|
{
|
// value是JObject类型
|
string jsonItem = JsonConvert.SerializeObject(kv.Value);
|
jsonItem = SortJson(jsonItem);
|
keyValues[kv.Key] = JsonConvert.DeserializeObject<JObject>(jsonItem);
|
}
|
else if (t1 == typeof(JArray))
|
{
|
StringBuilder stringBuilder = new StringBuilder();
|
stringBuilder.Append("[");
|
bool isFirst = true;
|
foreach (JToken item in (JArray)kv.Value)
|
{
|
if (item.Type == JTokenType.Object)
|
{
|
string jsonArrayItem = JsonConvert.SerializeObject(item);
|
jsonArrayItem = SortJson(jsonArrayItem);
|
if (isFirst)
|
{
|
stringBuilder.Append(jsonArrayItem);
|
}
|
else
|
{
|
stringBuilder.Append($",{jsonArrayItem}");
|
}
|
|
}
|
else if (item.Type == JTokenType.Array)
|
{
|
var arrayTmpRes = SortJson(JsonConvert.SerializeObject(item));
|
if (isFirst)
|
{
|
stringBuilder.Append(arrayTmpRes);
|
}
|
else
|
{
|
stringBuilder.Append($",{arrayTmpRes}");
|
}
|
}
|
else
|
{
|
string jsonArrayItem = JsonConvert.SerializeObject(item);
|
if (isFirst)
|
{
|
stringBuilder.Append(jsonArrayItem);
|
}
|
else
|
{
|
stringBuilder.Append($",{jsonArrayItem}");
|
}
|
}
|
isFirst = false;
|
}
|
stringBuilder.Append("]");
|
var tmpJsonStr = stringBuilder.ToString();
|
keyValues[kv.Key] = JsonConvert.DeserializeObject<JArray>(tmpJsonStr);
|
}
|
}
|
return JsonConvert.SerializeObject(keyValues);
|
}
|
else if (tokenType == JTokenType.Array)
|
{
|
var arraies = JsonConvert.DeserializeObject<JArray>(json);
|
StringBuilder stringBuilder2 = new StringBuilder();
|
stringBuilder2.Append("[");
|
bool isFirst2 = true;
|
foreach (JToken item in arraies)
|
{
|
if (item.Type == JTokenType.Object)
|
{
|
string jsonArrayItem = JsonConvert.SerializeObject(item);
|
jsonArrayItem = SortJson(jsonArrayItem);
|
if (isFirst2)
|
{
|
stringBuilder2.Append(jsonArrayItem);
|
}
|
else
|
{
|
stringBuilder2.Append($",{jsonArrayItem}");
|
}
|
|
}
|
else if (item.Type == JTokenType.Array)
|
{
|
var arrayTmpRes = SortJson(JsonConvert.SerializeObject(item));
|
if (isFirst2)
|
{
|
stringBuilder2.Append(arrayTmpRes);
|
}
|
else
|
{
|
stringBuilder2.Append($",{arrayTmpRes}");
|
}
|
}
|
else
|
{
|
string jsonArrayItem = JsonConvert.SerializeObject(item);
|
if (isFirst2)
|
{
|
stringBuilder2.Append(jsonArrayItem);
|
}
|
else
|
{
|
stringBuilder2.Append($",{jsonArrayItem}");
|
}
|
}
|
isFirst2 = false;
|
}
|
stringBuilder2.Append("]");
|
return stringBuilder2.ToString();
|
}
|
return json;
|
}
|
|
/// <summary>
|
/// 根据json判断json类型
|
/// </summary>
|
/// <param name="json"></param>
|
/// <returns></returns>
|
public static JTokenType GetJsonType(string json)
|
{
|
JToken token = JToken.Parse(json);
|
return token.Type;
|
}
|
|
|
|
#region MD5签名验证
|
/// <summary>
|
/// 对给定文件路径的文件加上标签
|
/// </summary>
|
/// <param name="path">要加密的文件的路径</param>
|
/// <returns>标签的值</returns>
|
public static bool AddMD5(string path)
|
{
|
bool IsNeed = true;
|
//已进行MD5处理
|
if (CheckMD5(path))
|
IsNeed = false;
|
|
try
|
{
|
FileStream fsread = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
byte[] md5File = new byte[fsread.Length];
|
// 将文件流读取到Buffer中
|
fsread.Read(md5File, 0, (int)fsread.Length);
|
fsread.Close();
|
|
if (IsNeed)
|
{
|
// 对Buffer中的字节内容算MD5
|
string result = MD5Buffer(md5File, 0, md5File.Length);
|
// 将字符串转换成字节数组以便写人到文件中
|
byte[] md5 = System.Text.Encoding.ASCII.GetBytes(result);
|
FileStream fsWrite = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
|
// 将文件,MD5值 重新写入到文件中。
|
fsWrite.Write(md5File, 0, md5File.Length);
|
fsWrite.Write(md5, 0, md5.Length);
|
fsWrite.Close();
|
}
|
else
|
{
|
FileStream fsWrite = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
|
fsWrite.Write(md5File, 0, md5File.Length);
|
fsWrite.Close();
|
}
|
}
|
catch
|
{
|
return false;
|
}
|
|
return true;
|
}
|
|
/// <summary>
|
/// 对给定路径的文件进行验证
|
/// </summary>
|
/// <param name="path"></param>
|
/// <returns>是否加了标签或是否标签值与内容值一致</returns>
|
public static bool CheckMD5(string path)
|
{
|
try
|
{
|
FileStream get_file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
// 读入文件
|
byte[] md5File = new byte[get_file.Length];
|
get_file.Read(md5File, 0, (int)get_file.Length);
|
get_file.Close();
|
// 对文件除最后32位以外的字节计算MD5,这个32是因为标签位为32位。
|
string result = MD5Buffer(md5File, 0, md5File.Length - 32);
|
//读取文件最后32位,其中保存的就是MD5值
|
string md5 = Encoding.ASCII.GetString(md5File, md5File.Length - 32, 32);
|
return result == md5;
|
}
|
catch
|
{
|
return false;
|
}
|
}
|
|
/// <summary>
|
/// 计算文件的MD5值
|
/// </summary>
|
/// <param name="MD5File">MD5签名文件字符数组</param>
|
/// <param name="index">计算起始位置</param>
|
/// <param name="count">计算终止位置</param>
|
/// <returns>计算结果</returns>
|
private static string MD5Buffer(byte[] MD5File, int index, int count)
|
{
|
System.Security.Cryptography.MD5CryptoServiceProvider get_md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
|
byte[] hash_byte = get_md5.ComputeHash(MD5File, index, count);
|
string result = BitConverter.ToString(hash_byte);
|
|
result = result.Replace("-", "");
|
return result;
|
}
|
#endregion
|
|
//自定义序列化工具类
|
public class JsonPropertySortResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
|
{
|
protected override IList<Newtonsoft.Json.Serialization.JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
{
|
return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
|
}
|
}
|
|
|
public class SortedCamelCaseContractResolver : DefaultContractResolver
|
{
|
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
{
|
// 获取所有需要序列化的属性(基类方法已过滤忽略属性等)
|
var properties = base.CreateProperties(type, memberSerialization).ToList();
|
|
// 先对每个属性应用 CamelCase 命名(确保最终输出的字段名是小驼峰)
|
foreach (var prop in properties)
|
{
|
prop.PropertyName = GetCamelCaseName(prop.UnderlyingName);
|
}
|
|
// 按属性名(即小驼峰后的名称)字母升序排序
|
return properties.OrderBy(p => p.PropertyName).ToList();
|
}
|
|
private string GetCamelCaseName(string name)
|
{
|
if (string.IsNullOrEmpty(name) || char.IsLower(name[0]))
|
return name;
|
return char.ToLower(name[0]) + name.Substring(1);
|
}
|
}
|
|
//入参加密前置格式化
|
public static string GetParamStr<T>(T objectParam)
|
{
|
// 获取JSON串,但排除sign字段
|
Func<object, string, object, bool> filter = (object obj, string field, object value) => !field.Equals("sign");
|
string paramStr = JsonConvert.SerializeObject(objectParam, new JsonSerializerSettings
|
{
|
ContractResolver = new SortedCamelCaseContractResolver(),
|
NullValueHandling = NullValueHandling.Ignore,
|
Formatting = Formatting.None,
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
DateFormatString = "yyyy-MM-dd HH:mm:ss",
|
DateTimeZoneHandling = DateTimeZoneHandling.Unspecified,
|
Converters = new List<JsonConverter> { new DecimalWithoutTrailingZerosConverter() }
|
});
|
//排序JOSN,得到格式化的串
|
return SortJson(paramStr);
|
}
|
|
public class DecimalWithoutTrailingZerosConverter : JsonConverter<decimal>
|
{
|
public override void WriteJson(JsonWriter writer, decimal value, JsonSerializer serializer)
|
{
|
// 判断是否为整数(尾数为零)
|
if (value == Math.Floor(value))
|
{
|
writer.WriteValue(Convert.ToInt64(value));
|
}
|
else
|
{
|
writer.WriteValue(value);
|
}
|
}
|
|
public override decimal ReadJson(JsonReader reader, Type objectType, decimal existingValue, bool hasExistingValue, JsonSerializer serializer)
|
{
|
// 反序列化时直接转换为 decimal,兼容整数和小数
|
return Convert.ToDecimal(reader.Value);
|
}
|
}
|
/// <summary>
|
/// 通用PLS接口签名校验
|
/// </summary>
|
public static bool CheckPLSSign<T>(T model) where T : class
|
{
|
try
|
{
|
// 固定配置
|
string localAppId = AppSettings.Get("LocalAppId");
|
string localAppSecret = AppSettings.Get("LocalAppSecret");
|
var type = typeof(T);
|
object signModel = null;
|
|
// ========== 自动获取公共字段 ==========
|
var timeStampProp = type.GetProperty("TimeStamp");
|
var signProp = type.GetProperty("Sign");
|
var dataProp = type.GetProperty("Data");
|
|
if (timeStampProp == null || signProp == null || dataProp == null)
|
return false;
|
|
// 获取值
|
string requestSign = signProp.GetValue(model)?.ToString() ?? "";
|
object timeStampVal = timeStampProp.GetValue(model);
|
object dataVal = dataProp.GetValue(model);
|
|
//时间戳校验
|
long nowTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
long timeStamp = 0;
|
|
if (timeStampVal is long lng)
|
timeStamp = lng;
|
else if (timeStampVal is string str && long.TryParse(str, out long t))
|
timeStamp = t;
|
|
//构建签名对象
|
if (localAppId != null)
|
{
|
signModel = new
|
{
|
AppId = localAppId,
|
TimeStamp = timeStamp,
|
Data = dataVal
|
};
|
}
|
else
|
{
|
return false;
|
}
|
|
string paramStr = GetParamStr(signModel);
|
string localSign = GetMD5_32(paramStr + localAppSecret);
|
///签名比对
|
return localSign.Equals(requestSign, StringComparison.OrdinalIgnoreCase);
|
}
|
catch
|
{
|
return false;
|
}
|
}
|
}
|
}
|