首页 > 解决方案 > 使用 DateTime Tick 计数生成 Nonce

问题描述

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

对于我使用的随机数生成

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

现在,在 100 个并发请求中,密钥被复制。密钥是如何复制的?

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

标签: c#nonce

解决方案


如果您有真正的并发请求,那么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)会截断随机数随机部分的高端位,确保次要值不与主要编号共享任何位。

我用一个shift32和产生的100_000_000值来运行它,并且只用尽了不到 5% 的可用数字long.MaxValue。只要你生产的随机数少于 20 亿,你就应该是好的。如果你想要更多然后减少shift


推荐阅读