因为之前的数据库的所有 id 字段都是使用的整数,现在需要对外暴露接口,所以需要对整数进行加密,变成类似于 guid 的形式。。
目的类似于 GUID, 对于相同类型相同ID的记录,总会产生相同的 guid,
例如:
"Id":"d34032f7bde406bfb8ef7447d898d0a2",
"PartnerId":"b8feb9b3db27f6d0d3204774b353d6e2"
估计看到的人都以为是 Guid , 也不会想着去解密了。。
/// <summary>
/// 随机的数,但是一旦确定,不能够改变;
/// </summary>
public static int[] Random = new int[]
{
123458817, 123459781, 345978112, 597811234, 781123459, 112345978, 477, 287,
1233645, 5415321, 11100, 7, 5423, 8172, 477, 21837,
1232145, 2541321, 1020, 8987, 5443, 887, 47927, 22867,
12312345, 5423211, 1040, 897, 54, 8782, 44747, 218,
};
/// <summary>
///
/// </summary>
public static char[] GuidArray = new char[]
{
'0', 'a', 'b','e','f','c','d','2','4','1','3','5','7','6','8','9'
};
/// <summary>
/// 根据类型和名称,相同的类型和 id 将产生相同的 guid,
/// </summary>
/// <param name="id"></param>
/// <param name="typeName"></param>
/// <returns></returns>
private static string ProductGuid(int id, string typeName)
{
char[] ran = new char[32];
var xId = Math.Abs(id ^ typeName.GetHashCode());
Random.ForEach((item, index) =>
{
var xx = Math.Abs(xId ^ item) % 16;
ran[index] = GuidArray[xx];
});
return new string(ran);
}
/// <summary>
/// 将Id藏起来到Guid中;返回加密后的字符串;
/// 对于相同类型的Id,会产生同样的 Guid;
/// 但是对于不同类型的相同Id,可能会产生不同的Guid;
/// </summary>
/// <param name="_id"></param>
/// <param name="debug"></param>
/// <returns></returns>
public static string CryptIdInGuid<T>(int? _id, bool debug = false)
{
if (!_id.HasValue || _id < 0) return null;
return CryptIdInGuid(_id, typeof(T), debug);
}
/// <summary>
/// 将Id藏起来到Guid中;返回加密后的字符串;
/// 如果 id 小于等于0,返回空;
/// 对于相同类型的相同Id,会产生同样的 Guid;
/// 对于相同类型的不同Id,会产生不同的 Guid;
/// 但是对于不同类型的不同Id,可能会产生相同的Guid;
/// </summary>
/// <param name="_id"></param>
/// <param name="type"></param>
/// <param name="debug"></param>
/// <returns></returns>
public static string CryptIdInGuid(int? _id, Type type, bool debug=false)
{
if (!_id.HasValue || _id < 0) return null;
var entityTypeName = ObjectContext.GetObjectType(type).Name;
return CryptIdInGuid(_id, entityTypeName, debug);
}
/// <summary>
/// 将Id藏起来到Guid中;返回加密后的字符串;
/// 如果 id 小于等于0,返回空;
/// 对于相同类型的相同Id,会产生同样的 Guid;
/// 但是对于不同类型的相同Id,可能会产生不同的Guid;
/// </summary>
/// <param name="_id"></param>
/// <param name="typeName"></param>
/// <param name="debug"></param>
/// <returns></returns>
private static string CryptIdInGuid(int? _id, string typeName, bool debug = false)
{
if (!_id.HasValue || _id < 0) return null;
var id = _id.Value;
string val = null;
var typeHash = Math.Abs(typeName.GetHashCode()); // 不要使用 Type,使用类型名称, 因为 Type的HashCode将根据内部值的不同而有所不同;
var guid = ProductGuid(id, typeName);
var longId = (id * 135721L);
var idVal = longId.ToString("x").ToLower().ToCharArray(); //id 转换成为16进制的字符数组;
//if (idVal.Length > 11) throw new Exception("数字溢出");
var bytes = guid.ToCharArray();
int beginPos = (id & typeHash) % 16; // new Random().Next(16);
bytes[bytes.Length - 1] = beginPos.ToString("x").ToCharArray()[0]; //倒数第1位用来表示起始位置;
var len = idVal.Length.ToString("x").ToCharArray()[0];
bytes[beginPos + 11] = len; //倒数第2位用来表示长度位置; 理论上不可能超过 16位长度
var crcString = ((id ^ 123456789) % (256 * 16)).ToString("x");
var crc = crcString.ToCharArray(); //校验字符数组位,3位长度;
if (crc.Length == 1)
{
crc = new char[] { '0', '0', crc[0] };
}
if (crc.Length == 2)
{
crc = new char[] { '0', crc[0], crc[1] };
}
Array.Copy(crc, 0, bytes, 28, crc.Length);
idVal.ForEach((chr, pos) =>
{
bytes[beginPos + pos] = chr;
});
val = new string(bytes);
if (debug)
{
LogUtil.xLogUtil.Warn(
"加密过程:{1}{2}type:{3}{2}typeHash:{4}{2}beginPos:{5}{2}len:{6}{2}sub:{7}{2}subLong:{8}{2}crc:{9}{2}id:{0}",
null,
id, val, Environment.NewLine, typeName, typeHash, beginPos, len, idVal, longId, crcString);
}
return val;
}
加解密效率约 1秒1000000次。。。