Як створити детерміновані посібники


103

У нашому додатку ми створюємо файли Xml з атрибутом, який має значення Guid. Це значення необхідно узгоджувати між оновленнями файлів. Тож навіть якщо все інше у файлі зміниться, орієнтирне значення для атрибута має залишатися незмінним.

Одним із очевидних рішень було створення статичного словника з назвою файлу та Посібниками, які будуть використовуватись для них. Тоді, коли ми генеруємо файл, ми шукаємо словник на ім'я файлу та використовуємо відповідне керівництво. Але це неможливо, оскільки ми можемо масштабувати до 100-ти файлів і не хочемо підтримувати великий список посібників.

Таким чином, інший підхід полягав у тому, щоб зробити Guid таким же на основі шляху файлу. Оскільки наші шляхи до файлів і структура каталогу додатків унікальні, Посібник повинен бути унікальним для цього шляху. Отже, кожного разу, коли ми запускаємо оновлення, файл отримує те саме керівництво на основі свого шляху. Я знайшов один класний спосіб створити такі « Детерміновані посібники » (спасибі Елтон Стоунмен). Це в основному так:

private Guid GetDeterministicGuid(string input) 

{ 

//use MD5 hash to get a 16-byte hash of the string: 

MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); 

byte[] inputBytes = Encoding.Default.GetBytes(input); 

byte[] hashBytes = provider.ComputeHash(inputBytes); 

//generate a guid from the hash: 

Guid hashGuid = new Guid(hashBytes); 

return hashGuid; 

} 

Таким чином, задавши рядок, Посібник завжди буде однаковим.

Чи є якісь інші підходи чи рекомендовані способи цього? Які плюси чи мінуси цього методу?

Відповіді:


151

Як зазначає @bacar, RFC 4122 §4.3 визначає спосіб створення UUID на основі імен. Перевага цього (ніж просто використання хешу MD5) полягає в тому, що вони гарантовано не стикаються з UUID, що не мають назви, та мають дуже малу можливість зіткнення з іншими UUID, заснованими на імені.

У .NET Framework немає вбудованої підтримки для їх створення, але я розмістив код на GitHub, який реалізує алгоритм. Його можна використовувати наступним чином:

Guid guid = GuidUtility.Create(GuidUtility.UrlNamespace, filePath);

Щоб ще більше знизити ризик зіткнення з іншими GUID, ви можете створити приватний GUID, який використовуватиметься як ідентифікатор простору імен (замість використання ідентифікатора простору імен URL, визначеного в RFC).


5
@Porges: RFC4122 неправильно і має помилки, які фіксують код C ( rfc-editor.org/errata_search.php?rfc=4122&eid=1352 ). Якщо ця реалізація не повністю відповідає RFC4122 та її помилкам, будь ласка, надайте подальші деталі; Я хотів би, щоб він відповідав стандарту.
Bradley Grainger

1
@BradleyGrainger: Я цього не помітив, дякую / вибачте! Я завжди повинен пам’ятати, щоб перевірити помилки під час читання RFC ... :)
porges

3
@Porges: Ласкаво просимо / немає проблем. Думає, що вони не оновлюють RFC на місці з виправленнями з помилок. Навіть посилання в кінці документа було б набагато кориснішим, ніж покладатися на читача, щоб пам'ятати, щоб шукати помилки (сподіваємось, перш ніж написати реалізацію на основі RFC ...).
Bradley Grainger

1
@BradleyGrainger: якщо ви використовуєте версію HTML, вона має посилання на помилку з заголовка, наприклад tools.ietf.org/html/rfc4122 . Цікаво, чи є розширення для браузера, яке завжди перенаправляє на версію HTML ...
порції

2
Вам слід розглянути можливість сприяти цьому .NET .NET repo тут: github.com/dotnet/coreclr/tree/master/src/mscorlib/src/System
sapphiremirage

29

Це перетворить будь-який рядок у керівництво без необхідності імпорту зовнішньої збірки.

public static Guid ToGuid(string src)
{
    byte[] stringbytes = Encoding.UTF8.GetBytes(src);
    byte[] hashedBytes = new System.Security.Cryptography
        .SHA1CryptoServiceProvider()
        .ComputeHash(stringbytes);
    Array.Resize(ref hashedBytes, 16);
    return new Guid(hashedBytes);
}

Є набагато кращі способи створення унікального Guid, але це спосіб послідовного оновлення рядкового ключа даних до ключа даних Guid.


Цей фрагмент виявився корисним при використанні унікального ідентифікатора в базі даних для федеративного розподілу.
Глено

6
Увага! Цей код не створює дійсних Посібників / UUID (як bacar також згадується нижче). Ні версія, ні поле типу не встановлені правильно.
MarkusSchaber

3
Чи не було б настільки ж ефективним використання MD5CryptoServiceProvider замість SHA1, оскільки в MD5 вже 16 байт?
Brain2000

20

Як згадує Роб, ваш метод не генерує UUID, він генерує хеш, схожий на UUID.

RFC 4122 на UUID , в зокрема , дозволяє детермінованих (ім'я) на основі UUID , - версії 3 і 5 Використання MD5 і SHA1 (відповідно). Більшість людей, напевно, знайомі з версією 4, яка є випадковою. Вікіпедія дає хороший огляд версій. (Зверніть увагу, що використання слова "версія" тут, схоже, описує "тип" UUID - версія 5 не витісняє версію 4).

Здається, існує декілька бібліотек для генерування UUID версії 3/5, включаючи модуль python uuid , boost.uuid (C ++) та OSSP UUID . (Я не шукав жодної .net)


1
Це саме те, про що йде оригінальний плакат. UUID вже має алгоритм для початку з рядка та перетворення його в GUID. UUID версія 3 містить хеш-рядок з MD5, а версія 5 - з SHA1. Важливим моментом у створенні "настанови" є зробити його "унікальним" щодо інших GUID. Алгоритм визначає два біти, які потрібно встановити, а також nibble встановлюється на 3 або 5, залежно від версії 3 чи 5.
Ian Boyd

2
Щодо використання слова "версія", RFC 4122 §4.1.3 зазначає: "Версія є більш точним підтипом; знову ж таки ми зберігаємо термін сумісності".
Бредлі Грейнджер

11
Я опублікував декілька кодів C #, щоб створити v3 та v5 GUID на GitHub: github.com/LogosBible/Logos.Utility/blob/master/src/…
Бредлі

@BradleyGrainger, я отримую Warning Bitwise - або оператор використовується на операнді з розширеним знаком; спершу розгляньте кастинг на менший тип без підпису
Себастьян

1
Це стає поза темою! Запропонуйте GitHub звітувати про помилки, пов’язані з переміщенням окремих ліб.
бакар

3

Потрібно розрізняти екземпляри класу Guidта ідентифікатори, які є унікальними у всьому світі. "Детерміновані настанови" - це фактично хеш (про що свідчить ваш заклик provider.ComputeHash). Хеши мають набагато більший шанс зіткнення (дві різні рядки, що трапляються для того ж хеша), ніж у Guid, створеного через Guid.NewGuid.

Тож проблема вашого підходу полягає в тому, що вам доведеться бути в порядку з можливістю, що два різних контури будуть створювати один і той же GUID. Якщо вам потрібен ідентифікатор, унікальний для будь-якої заданої рядки шляху, то найпростіше зробити це просто використовувати рядок . Якщо вам потрібно затінити рядок від своїх користувачів, зашифруйте її - ви можете використовувати ROT13 або щось більш потужне ...

Спроба вбудувати щось, що не є чистим GUID у типі даних GUID, може призвести до проблем з технічним обслуговуванням у майбутньому ...


2
Ви стверджуєте, що "хеши мають набагато більший шанс зіткнення ..., ніж Guid, створений через Guid.NewGuid." Чи можете ви детальніше розглянути це? З математичної точки зору кількість бітів, які можна встановити, однакова, і обидва MD5 і SHA1 є криптографічними хешами, спеціально розробленими для зниження ймовірності (випадкових і навмисних) хеш-зіткнень.
MarkusSchaber

Я б сказав, що головна відмінність - це криптографічна хеш-карта від одного нескінченного простору до іншого нерухомого простору за допомогою функції. Зображення хеша, який відображає рядки змінної довжини до 128 біт, тоді як Guid генерує псевдовипадкові 128 біт. Псевдовипадкова генерація не покладається на початковий вхід, а скоріше шляхом генерування виводу рівномірно у вихідному просторі, використовуючи випадковість, посіяну з обладнання або інших засобів.
Тай Буй

2

MD5 слабкий, я вважаю, що ви можете зробити те ж саме з SHA-1 і отримати кращі результати.

BTW, лише особиста думка, одягання хеш-файлу md5 як GUID не робить його хорошим GUID. GUID за своєю суттю не детерміновані. це відчувається як обман. Чому б просто не називати лопату лопатою, а просто сказати, що її рядок надає хеш вводу. ви можете зробити це за допомогою цього рядка, а не нової лінії:

string stringHash = BitConverter.ToString(hashBytes)

Дякую за ваш внесок, але це все ще дає мені рядок, і я шукаю посібник ...
Карайте Вору,

Гаразд, назвіть свій хеш "GUID", проблема вирішена. Або це реальна проблема , що ви потрібні в Guidоб'єкт?
user7116

Я б хотів, щоб це було так просто .. :) але так, мені потрібен об’єкт 'GUID'
карайте Вору,

5
"GUID за своєю суттю не є детермінованими" - це стосується лише певних типів ("версій") GUID. Однак я погоджуюся, що "одягання хеш-файлу md5 як GUID не робить хорошого GUID" з інших причин, як це написано @Bradley Grainger та @Rob Fonseca-Ensor, і моя відповідь на це питання.
бакар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.