【问题标题】:Is there any way to generate the same UUID from a String有什么方法可以从字符串生成相同的 UUID
【发布时间】:2015-05-17 13:06:51
【问题描述】:

我想知道是否有办法根据String. 生成相同的 UUID 我用 UUID 试过了,好像没有这个功能。

【问题讨论】:

    标签: java uuid


    【解决方案1】:

    UUID.nameUUIDFromBytes() 方法生成 MD5 UUID。 SHA1 优于 MD5,如果向后兼容性不是问题

    这是一个生成 MD5 和 SHA1 UUID 的实用程序类。它还支持 UUID.nameUUIDFromBytes() 方法不支持的命名空间,尽管 RFC-4122 要求。随意使用和分享。

    package your.package.name;
    
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.UUID;
    
    /**
     * Utility class that creates UUIDv3 (MD5) and UUIDv5 (SHA1).
     *
     */
    public class HashUuidCreator {
    
        // Domain Name System
        public static final UUID NAMESPACE_DNS = new UUID(0x6ba7b8109dad11d1L, 0x80b400c04fd430c8L);
        // Uniform Resource Locator
        public static final UUID NAMESPACE_URL = new UUID(0x6ba7b8119dad11d1L, 0x80b400c04fd430c8L);
        // ISO Object ID
        public static final UUID NAMESPACE_ISO_OID = new UUID(0x6ba7b8129dad11d1L, 0x80b400c04fd430c8L);
        // X.500 Distinguished Name
        public static final UUID NAMESPACE_X500_DN = new UUID(0x6ba7b8149dad11d1L, 0x80b400c04fd430c8L);
    
        private static final int VERSION_3 = 3; // UUIDv3 MD5
        private static final int VERSION_5 = 5; // UUIDv5 SHA1
    
        private static final String MESSAGE_DIGEST_MD5 = "MD5"; // UUIDv3
        private static final String MESSAGE_DIGEST_SHA1 = "SHA-1"; // UUIDv5
    
        private static UUID getHashUuid(UUID namespace, String name, String algorithm, int version) {
    
            final byte[] hash;
            final MessageDigest hasher;
    
            try {
                // Instantiate a message digest for the chosen algorithm
                hasher = MessageDigest.getInstance(algorithm);
    
                // Insert name space if NOT NULL
                if (namespace != null) {
                    hasher.update(toBytes(namespace.getMostSignificantBits()));
                    hasher.update(toBytes(namespace.getLeastSignificantBits()));
                }
    
                // Generate the hash
                hash = hasher.digest(name.getBytes(StandardCharsets.UTF_8));
    
                // Split the hash into two parts: MSB and LSB
                long msb = toNumber(hash, 0, 8); // first 8 bytes for MSB
                long lsb = toNumber(hash, 8, 16); // last 8 bytes for LSB
    
                // Apply version and variant bits (required for RFC-4122 compliance)
                msb = (msb & 0xffffffffffff0fffL) | (version & 0x0f) << 12; // apply version bits
                lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // apply variant bits
    
                // Return the UUID
                return new UUID(msb, lsb);
    
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("Message digest algorithm not supported.");
            }
        }
    
        public static UUID getMd5Uuid(String string) {
            return getHashUuid(null, string, MESSAGE_DIGEST_MD5, VERSION_3);
        }
    
        public static UUID getSha1Uuid(String string) {
            return getHashUuid(null, string, MESSAGE_DIGEST_SHA1, VERSION_5);
        }
    
        public static UUID getMd5Uuid(UUID namespace, String string) {
            return getHashUuid(namespace, string, MESSAGE_DIGEST_MD5, VERSION_3);
        }
    
        public static UUID getSha1Uuid(UUID namespace, String string) {
            return getHashUuid(namespace, string, MESSAGE_DIGEST_SHA1, VERSION_5);
        }
    
        private static byte[] toBytes(final long number) {
            return new byte[] { (byte) (number >>> 56), (byte) (number >>> 48), (byte) (number >>> 40),
                    (byte) (number >>> 32), (byte) (number >>> 24), (byte) (number >>> 16), (byte) (number >>> 8),
                    (byte) (number) };
        }
    
        private static long toNumber(final byte[] bytes, final int start, final int length) {
            long result = 0;
            for (int i = start; i < length; i++) {
                result = (result << 8) | (bytes[i] & 0xff);
            }
            return result;
        }
    
        /**
         * For tests!
         */
        public static void main(String[] args) {
    
            String string = "JUST_A_TEST_STRING";
            UUID namespace = UUID.randomUUID(); // A custom name space
    
            System.out.println("Java's generator");
            System.out.println("UUID.nameUUIDFromBytes():      '" + UUID.nameUUIDFromBytes(string.getBytes()) + "'");
            System.out.println();
            System.out.println("This generator");
            System.out.println("HashUuidCreator.getMd5Uuid():  '" + HashUuidCreator.getMd5Uuid(string) + "'");
            System.out.println("HashUuidCreator.getSha1Uuid(): '" + HashUuidCreator.getSha1Uuid(string) + "'");
            System.out.println();
            System.out.println("This generator WITH name space");
            System.out.println("HashUuidCreator.getMd5Uuid():  '" + HashUuidCreator.getMd5Uuid(namespace, string) + "'");
            System.out.println("HashUuidCreator.getSha1Uuid(): '" + HashUuidCreator.getSha1Uuid(namespace, string) + "'");
        }
    }
    
    

    这是输出:

    // Java's generator
    UUID.nameUUIDFromBytes():      '9e120341-627f-32be-8393-58b5d655b751'
    
    // This generator
    HashUuidCreator.getMd5Uuid():  '9e120341-627f-32be-8393-58b5d655b751'
    HashUuidCreator.getSha1Uuid(): 'e4586bed-032a-5ae6-9883-331cd94c4ffa'
    
    // This generator WITH name space (as the standard requires)
    HashUuidCreator.getMd5Uuid():  '2b098683-03c9-3ed8-9426-cf5c81ab1f9f'
    HashUuidCreator.getSha1Uuid(): '1ef568c7-726b-58cc-a72a-7df173463bbb'
    

    您还可以使用uuid-creator 库。看这个例子:

    // Create a name based UUID (SHA1)
    String name = "JUST_A_TEST_STRING";
    UUID uuid = UuidCreator.getNameBasedSha1(name);
    

    项目页面:https://github.com/f4b6a3/uuid-creator

    【讨论】:

    • 为什么您认为在生成 UUID 时应该首选 SHA1 而不是 MD5?
    • 我认为它不应该总是首选。这取决于具体情况。 RFC-4122 在其第 4.3 节中说,如果向后兼容性不是问题,则首选 SHA-1。我会修正我的评论。谢谢。
    【解决方案2】:

    您应该使用 UUID v5。

    第 3 版和第 5 版 UUID 是通过对命名空间标识符和名称进行散列生成的。版本 3 使用 MD5 作为散列算法,版本 5 使用 SHA-1.1 - wikipedia

    UUID v5 需要一个命名空间。该命名空间应该是一个 UUID v4,您可以使用 generate online。命名空间确保对于给定的输入,输出总是相同的。

    可以找到 UUID v5 的可能实现 here:

    <!-- https://search.maven.org/artifact/com.github.f4b6a3/uuid-creator -->
    <dependency>
      <groupId>com.github.f4b6a3</groupId>
      <artifactId>uuid-creator</artifactId>
      <version>3.6.0</version>
    </dependency>
    

    可以这样使用:

    UUID namespace = ; // todo generate a UUID v4.
    String input = "input";
    UUID uuid = UuidCreator.getNameBasedSha1(namespace, input);
    

    (在某种程度上,对于随机数生成器,命名空间就像种子一样。相比之下,虽然种子应该是随机的,但我们的命名空间是一个常数。这迫使我们的生成器总是为给定的输入产生相同的值。)

    【讨论】:

      【解决方案3】:

      如果您正在寻找 Javascript 替代方案,请查看 uuid-by-string,它还提供了使用 SHA-1 或 MD5 哈希函数的选项。

      【讨论】:

        【解决方案4】:

        您可以通过这种方式使用 UUID 为您的输入字符串获取始终相同的 UUID:

         String aString="JUST_A_TEST_STRING";
         String result = UUID.nameUUIDFromBytes(aString.getBytes()).toString();
        

        【讨论】:

        • 任何 js 等效项?
        • @mika This PHP UUID library 有点等价。您可以为给定的命名空间 + 字符串生成相同的 UUID。您可以执行以下操作:Uuid::uuid3(Uuid::NAMESPACE_DNS, 'TEST STRING')-&gt;toString(); 在此示例中它使用 md5 散列。 Additional info on UUID namespaces
        • 有什么办法可以将此 UUID 解码为原始字符串?
        • 从给定字符串生成的 UUID 与从另一个字符串生成的 UUID 发生冲突的可能性有多大?
        • @Groppe 非常小,类似于给定字符串的 MD5 (UUIDv3) 或 SHA1 (UUIDv5) 哈希冲突的可能性
        猜你喜欢
        • 2015-01-18
        • 2015-05-16
        • 1970-01-01
        • 2020-07-30
        • 2021-09-17
        • 1970-01-01
        • 2011-10-27
        • 2012-01-30
        • 1970-01-01
        相关资源
        最近更新 更多