Deterministik Kılavuzlar Nasıl Oluşturulur


107

Uygulamamızda, Guid değeri olan bir özniteliğe sahip Xml dosyaları oluşturuyoruz. Bu değerin dosya yükseltmeleri arasında tutarlı olması gerekiyordu. Dolayısıyla, dosyadaki diğer her şey değişse bile, özniteliğin kılavuz değeri aynı kalmalıdır.

Açık bir çözüm, dosya adı ve bunlar için kullanılacak Kılavuzlar ile statik bir sözlük oluşturmaktı. Daha sonra, dosyayı her oluşturduğumuzda, dosya adı için sözlüğe bakar ve ilgili kılavuzu kullanırız. Ancak bu mümkün değil çünkü 100'lerce dosyaya ölçeklenebilir ve büyük bir kılavuz listesi tutmak istemedik.

Bu nedenle başka bir yaklaşım, Kılavuz'u dosyanın yoluna bağlı olarak aynı yapmaktı. Dosya yollarımız ve uygulama dizini yapımız benzersiz olduğundan, Kılavuz bu yol için benzersiz olmalıdır. Dolayısıyla, bir yükseltme çalıştırdığımızda, dosya yoluna bağlı olarak aynı kılavuzu alır. Bu tür ' Belirleyici Kılavuzlar ' oluşturmanın harika bir yolunu buldum (Teşekkürler Elton Stoneman). Temelde şunu yapar:

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; 

} 

Dolayısıyla bir dizge verildiğinde Kılavuz her zaman aynı olacaktır.

Bunu yapmak için başka yaklaşımlar veya önerilen yollar var mı? Bu yöntemin artıları veya eksileri nelerdir?

Yanıtlar:


154

@Bacar'da belirtildiği gibi, RFC 4122 §4.3 isme dayalı bir UUID yaratmanın bir yolunu tanımlar. Bunu yapmanın avantajı (sadece bir MD5 karması kullanmaktan ziyade), bunların adlandırılmayan temelli UUID'lerle çakışmamalarının garanti edilmesi ve diğer isme dayalı UUID'lerle çok (çok) küçük bir çarpışma olasılığına sahip olmalarıdır.

.NET Framework'te bunları oluşturmak için yerel destek yok, ancak algoritmayı uygulayan kodu GitHub'da yayınladım . Aşağıdaki şekilde kullanılabilir:

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

Diğer GUID'lerle çakışma riskini daha da azaltmak için, ad alanı kimliği olarak kullanmak üzere özel bir GUID oluşturabilirsiniz (RFC'de tanımlanan URL ad alanı kimliğini kullanmak yerine).


5
@Porges: RFC4122 yanlış ve C kodunu düzelten hata verileri içeriyor ( rfc-editor.org/errata_search.php?rfc=4122&eid=1352 ). Bu uygulama RFC4122 ve onun hata verileriyle tam olarak uyumlu değilse, lütfen daha fazla ayrıntı sağlayın; Standardı takip etmesini istiyorum.
Bradley Grainger

1
@BradleyGrainger: Bunu fark etmedim, teşekkürler / üzgünüm! Bir RFC okurken
hataları

3
@ Porges: rica ederim / sorun değil. Hatalardan gelen düzeltmelerle RFC'yi yerinde güncellememeleri akılları şaşırtıyor. Belgenin sonundaki bir bağlantı bile okuyucunun yazım hatalarını aramayı hatırlamasına güvenmekten çok daha yararlı olacaktır (umarım RFC'ye dayalı bir uygulama yazmadan önce ...).
Bradley Grainger

1
@BradleyGrainger: HTML sürümünü kullanıyorsanız, başlıktaki hata verisine bir bağlantı vardır, örneğin, tools.ietf.org/html/rfc4122 . Her zaman HTML sürümüne yönlendirilecek bir tarayıcı uzantısı olup olmadığını merak ediyorum ...
2013

3
Bunu .NET .NET deposuna
sapphiremirage

29

Bu, herhangi bir dizeyi bir dış derlemeyi içe aktarmak zorunda kalmadan bir Kılavuza dönüştürecektir.

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

Benzersiz bir Kılavuz oluşturmanın çok daha iyi yolları vardır, ancak bu, bir dizi veri anahtarını bir Kılavuz veri anahtarına tutarlı bir şekilde yükseltmenin bir yoludur.


Bu kod parçacığı, federe dağıtım için bir veritabanında benzersiz tanımlayıcı kullanırken faydalı olacak şekilde bulundu.
Gleno

6
Uyarı! Bu kod, geçerli Kılavuzlar / UUID'ler üretmez (aşağıda da belirtildiği gibi). Ne sürüm ne de tür alanı doğru ayarlanmamış.
MarkusSchaber

3
MD5 zaten 16 bayt uzunluğunda olduğundan, SHA1 yerine MD5CryptoServiceProvider'ı kullanmak o kadar etkili olmaz mıydı?
Brain2000

21

Rob'un bahsettiği gibi, yönteminiz bir UUID oluşturmaz, UUID'ye benzeyen bir karma oluşturur.

RFC 4122 Versiyonlar 3 ve 5 MD5 ve SHA1 (sırasıyla) - UUID üzerinde özellikle deterministik (ad bazlı) UUID sağlar. Çoğu kişi muhtemelen rastgele olan sürüm 4'e aşinadır. Wikipedia , sürümler hakkında iyi bir genel bakış sunar. (Burada 'sürüm' kelimesinin kullanımının bir UUID 'türünü' tanımladığını unutmayın - sürüm 5, sürüm 4'ün yerine geçmez).

Python uuid modülü , boost.uuid (C ++) ve OSSP UUID dahil olmak üzere sürüm 3/5 UUID'leri oluşturmak için birkaç kitaplık var gibi görünüyor . (.Net olanları aramadım)


1
Orijinal afiş tam olarak bunun peşinde. UUID, bir dizeyle başlayıp bunu GUID'ye dönüştürmeniz için zaten bir algoritmaya sahiptir. UUID sürüm 3, dizeyi MD5 ile karma hale getirirken, sürüm 5 SHA1 ile karma oluşturur. Bir "kılavuz" oluşturmanın önemli noktası, onu diğer GUID'lere karşı "benzersiz" kılmaktır. Algoritma, ayarlanması gereken iki biti tanımlar ve bir yarım bayt, sürüm 3 veya 5'e bağlı olarak 3 veya 5'e ayarlanır.
Ian Boyd

2
"Sürüm" kelimesinin kullanımıyla ilgili olarak, RFC 4122 §4.1.3 şunu belirtir: "Sürüm, daha doğru bir şekilde bir alt tür; yine, uyumluluk terimini koruyoruz."
Bradley Grainger

11
GitHub'da v3 ve v5 GUID'leri oluşturmak için bazı C # kodu gönderdim: github.com/LogosBible/Logos.Utility/blob/master/src/…
Bradley Grainger

@BradleyGrainger, Uyarı Bitwise'ı veya işaretle genişletilmiş bir işlenende kullanılan işleci alıyorum; önce daha küçük bir işaretsiz türe yayınlamayı düşünün
Sebastian

1
Bu konu dışına çıkıyor! Bireysel kitaplık hata raporlarının GitHub'a taşınmasını önerin.
13'te bacar

3

Sınıfın örnekleri Guidile genel olarak benzersiz olan tanımlayıcılar arasında bir ayrım yapmanız gerekir . Bir "deterministik kılavuz" aslında bir karmadır (çağrınız ile kanıtlandığı gibi provider.ComputeHash). Hash'lerin, Guid aracılığıyla oluşturulmuş olduğundan çok daha yüksek bir çarpışma şansı vardır (aynı hash'i üretmek için iki farklı dizge) Guid.NewGuid.

Bu nedenle, yaklaşımınızla ilgili sorun, iki farklı yolun aynı GUID'yi üretme olasılığını göz önünde bulundurmanız gerekmesidir. Herhangi bir yol dizesi için benzersiz olan bir tanımlayıcıya ihtiyacınız varsa, yapılacak en kolay şey dizeyi kullanmaktır . Dizenin kullanıcılarınızdan gizlenmesine ihtiyacınız varsa, şifreleyin - ROT13 veya daha güçlü bir şey kullanabilirsiniz ...

GUID veri türüne saf bir GUID olmayan bir şeyi çekmeye çalışmak, gelecekte bakım sorunlarına yol açabilir ...


2
"Hash'ların, Guid'in Guid.NewGuid ile oluşturduğundan çok daha yüksek bir çarpışma şansına sahip olduğunu" iddia ediyorsunuz. Bunu detaylandırır mısın? Matematiksel bir bakış açısından, ayarlanabilen bit sayısı aynıdır ve hem MD5 hem de SHA1, hash çarpışmaları olasılığını azaltmak için özel olarak tasarlanmış kriptografik karmalardır.
MarkusSchaber

Temel farkın, bir sonsuz boşluktan başka bir sabit alana bir işlev kullanarak kriptografik karma haritası olduğunu söyleyebilirim. Guid sözde rasgele 128 bit üretirken, değişken uzunluklu dizeleri 128 bit ile eşleyen bir hash görüntüleme. Sözde rasgele oluşturma, bir ilk girdiye dayanmaz, bunun yerine, donanımdan veya başka yollardan tohumlanan rasgeleliği kullanarak çıktıyı çıktı uzayında tekdüze bir şekilde oluşturarak.
Thai Bui

1

MD5 zayıf, aynı şeyi SHA-1 ile yapıp daha iyi sonuçlar alabileceğinize inanıyorum.

BTW, sadece kişisel bir görüş, bir md5 karmasını bir GUID olarak giydirmek onu iyi bir GUID yapmaz. GUID'ler doğaları gereği Belirleyici değildir. bu bir hile gibi geliyor. Neden bir spade'i sadece bir spade olarak adlandırıp, sadece girdinin bir string render edilmiş hash'i söylemiyorsunuz? bunu yeni kılavuz satırı yerine bu satırı kullanarak yapabilirsiniz:

string stringHash = BitConverter.ToString(hashBytes)

Girişiniz için teşekkürler, ancak bu hala bana bir dize veriyor ve bir GUID arıyorum ...
Punit Vora

Tamam, hashinize bir "GUID" deyin, sorun çözüldü. Yoksa gerçek sorun bir nesneye mi ihtiyacınız varGuid ?
user7116

keşke bu kadar basit olsaydı .. :) ama evet, bir 'GUID' nesnesine ihtiyacım var
Punit Vora

6
"GUID'ler doğaları gereği Belirleyici değildir" - bu yalnızca belirli GUID türleri ('sürümleri') için geçerlidir. Bununla birlikte, @Bradley Grainger ve @Rob Fonseca-Ensor tarafından dile getirilen diğer nedenlerden ve bu soruya verdiğim cevabın "md5 karmasını bir GUID olarak giydirmenin iyi bir GUID oluşturmayacağını" kabul ediyorum.
bacar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.