Генерация одноразовых номеров с датой DateTime - PullRequest
0 голосов
/ 03 апреля 2019

В нашем веб-приложении мы используем внешний сервис для определенной функции. Чтобы запросить эту внешнюю службу, мы должны добавить key в заголовок запроса, который является целым числом и уникальным для всех моих запросов, технически позвоните nonce

для генерации nonce я использовал

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

Теперь в 100 одновременных запросах ключ дублируется. Как дублируется ключ?

Я не могу использовать GUID, потому что мне нужно постоянно увеличивать целочисленное значение.

Ответы [ 2 ]

1 голос
/ 04 апреля 2019

Если у вас действительно параллельные запросы, то 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 32 и произвел 100_000_000 значений и исчерпал только менее 5% доступных чисел в направлении long.MaxValue.Пока вы производите менее 2 миллиардов одноразовых номеров, вы должны быть хорошими.Если вы хотите больше, уменьшите shift.

0 голосов
/ 03 апреля 2019

Полагаю, проблема в том, что random.Next(100).

Согласно документации , random.Next возвращает неотрицательное значение, которое меньше 100. После 100 одновременных запросов этим методом может возвращаться то же значение.

Как указано в комментариях выше, вы должны использовать GUID, так как вероятность того, что они будут одинаковыми, крайне мала.

Или, если вы не хотите использовать GUID, попробуйте следующий код (взято из этого блога ).

Это должно помочь вам решить проблему.

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

Надеюсь, это поможет.

...