在我们的Web应用程序中,我们将外部服务用于特定功能。要请求该外部服务,我们必须在请求标头中添加一个key
,该标头是整数,并且对我的所有请求都是唯一的,从技术上讲,称为nonce
我曾经使用过的随机数
var nonce = (long) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).Ticks*100 + random.Next(100);
现在,在100个并发请求中,密钥正在重复。密钥如何复制?
我不能使用GUID,因为我需要不断增加整数值。
答案 0 :(得分:1)
如果您有真正的并发请求,那么DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).Ticks * 100
是这些请求的常量。然后,您只剩下random.Next(100)
,然后就不需要很多碰撞了。一个简单(但不理想)的选择是只做random.Next()
。
一个更好的主意是:
[ThreadStatic]
private static Random __random = new Random();
private static int shift = 32;
private static long counter = 0L;
public long GenerateNextNonce()
{
var major = ++counter << shift;
var minor = (DateTime.UtcNow.Ticks ^ __random.Next()) & (1L << shift - 1);
return major + minor;
}
++counter
确保您拥有越来越多的数字序列-仅此一项就足以产生现时数,但它是高度可预测的,因此容易遭到黑客攻击。
计算DateTime.UtcNow.Ticks ^ __random.Next()
可确保一个相当随机的数字,它不完全依赖于Random
的实现,因此可确保该数字高度不可预测,但不一定不断增加。
使用shift
值可确保将counter
值移到数字的“高端”或主要部分。调用(DateTime.UtcNow.Ticks ^ __random.Next()) & (1L << shift - 1)
会截断随机数随机部分的高端位,以确保次要值不与主数共享任何位。
我用shift
的{{1}}值运行了此操作,并产生了32
值,并且仅用尽了朝着100_000_000
可用数字的5%。只要您产生的随机数少于20亿,就应该很好。如果您想要更多,请减少long.MaxValue
。
答案 1 :(得分:0)
我想问题是因为random.Next(100)
。
根据documentation,random.Next返回小于100的非负值。 在并发请求100次之后,此方法可能会返回相同的值。
如以上注释中所建议,您应该选择GUID,因为它们相同的可能性极低。
否则,如果您不想使用GUID,则可以尝试以下代码(摘自blog)。
它应该可以帮助您解决问题。
public static string GetNonce()
{
// better to get unique random number if
// called mulitple times
Random r = RandomProvider.GetThreadRandom();
DateTime created = DateTime.Now;
string nonce = Convert.ToBase64String(Encoding.ASCII.GetBytes(SHA1Encrypt(created + r.Next().ToString())));
return nonce;
}
希望这会有所帮助。