使用DateTime的滴答计数立即生成

时间:2019-04-03 06:41:44

标签: c# nonce

在我们的Web应用程序中,我们将外部服务用于特定功能。要请求该外部服务,我们必须在请求标头中添加一个key,该标头是整数,并且对我的所有请求都是唯一的,从技术上讲,称为nonce

我曾经使用过的随机数

var nonce = (long) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).Ticks*100 + random.Next(100);

现在,在100个并发请求中,密钥正在重复。密钥如何复制?

我不能使用GUID,因为我需要不断增加整数值。

2 个答案:

答案 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;
}

希望这会有所帮助。