有没有一种方法可以从字符串生成相同的UUID?

130

我想知道是否有一种方法可以基于一个字符串生成相同的UUID。我尝试使用UUID,但它似乎没有提供此功能。

4个回答

235
您可以按以下方式使用UUID,以获取输入字符串的始终相同的UUID:
 String aString="JUST_A_TEST_STRING";
 String result = UUID.nameUUIDFromBytes(aString.getBytes()).toString();

16
有任何JS的等效替代方案吗? - Abhijeet Ahuja
4
这个PHP UUID库(https://github.com/ramsey/uuid)有点类似。你可以为给定的命名空间+字符串生成相同的UUID。你可以这样做:`Uuid::uuid3(Uuid::NAMESPACE_DNS, 'TEST STRING')->toString();`它在这个例子中使用了md5散列。关于UUID命名空间的其他信息,请参见(https://dev59.com/xmgv5IYBdhLWcg3wD8uY#28776880)。 - segFault
4
我能否将此UUID解码为原始字符串? - Mayur
2
如果原始字符串是已知字符串集合的一部分(例如存储在您的数据库中),则可以为每个字符串生成UUID并与要解码的UUID进行比较。否则,从“技术上”来说是不可能的。 - user108828
4
给定一个字符串生成的UUID与另一个字符串生成的UUID发生冲突的几率有多大? - Groppe
显示剩余6条评论

18

UUID.nameUUIDFromBytes()只会生成MD5 UUID。然而,如果不需要向后兼容,建议使用SHA1而非MD5。

下面的实用类可以生成MD5 UUID和SHA-1 UUID。欢迎使用和分享。

package com.example;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * Generates UUIDv3 (MD5) and UUIDv5 (SHA1).
 *
 * It is fully compliant with RFC-4122.
 */
public class HashUuid {

    private static final int V3 = 3; // MD5
    private static final int V5 = 5; // SHA-1

    private static final String HASH_V3 = "MD5";
    private static final String HASH_V5 = "SHA-1";

    public static final UUID NAMESPACE_DNS = new UUID(0x6ba7b8109dad11d1L, 0x80b400c04fd430c8L);
    public static final UUID NAMESPACE_URL = new UUID(0x6ba7b8119dad11d1L, 0x80b400c04fd430c8L);
    public static final UUID NAMESPACE_OID = new UUID(0x6ba7b8129dad11d1L, 0x80b400c04fd430c8L);
    public static final UUID NAMESPACE_X500 = new UUID(0x6ba7b8149dad11d1L, 0x80b400c04fd430c8L);

    public static UUID v3(String name) {
        return generate(V3, HASH_V3, null, name);
    }

    public static UUID v5(String name) {
        return generate(V5, HASH_V5, null, name);
    }

    public static UUID v3(UUID namespace, String name) {
        return generate(V3, HASH_V3, namespace, name);
    }

    public static UUID v5(UUID namespace, String name) {
        return generate(V5, HASH_V5, namespace, name);
    }

    private static UUID generate(int version, String algorithm, UUID namespace, String name) {

        MessageDigest hasher = hasher(algorithm);

        if (namespace != null) {
            ByteBuffer ns = ByteBuffer.allocate(16);
            ns.putLong(namespace.getMostSignificantBits());
            ns.putLong(namespace.getLeastSignificantBits());
            hasher.update(ns.array());
        }

        hasher.update(name.getBytes(StandardCharsets.UTF_8));
        ByteBuffer hash = ByteBuffer.wrap(hasher.digest());

        final long msb = (hash.getLong() & 0xffffffffffff0fffL) | (version & 0x0f) << 12;
        final long lsb = (hash.getLong() & 0x3fffffffffffffffL) | 0x8000000000000000L;

        return new UUID(msb, lsb);
    }

    private static MessageDigest hasher(String algorithm) {
        try {
            return MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(String.format("%s not supported.", algorithm));
        }
    }

    /**
     * For tests!
     */
    public static void main(String[] args) {

        UUID namespace = UUID.randomUUID();
        String name = "JUST_A_TEST_STRING";

        System.out.println(String.format("UUID.nameUUIDFromBytes():     '%s'", UUID.nameUUIDFromBytes(name.getBytes())));
        System.out.println();
        System.out.println(String.format("HashUuid.v3(name):            '%s'", HashUuid.v3(name)));
        System.out.println(String.format("HashUuid.v5(name):            '%s'", HashUuid.v5(name)));
        System.out.println(String.format("HashUuid.v3(namespace, name): '%s'", HashUuid.v3(namespace, name)));
        System.out.println(String.format("HashUuid.v5(namespace, name): '%s'", HashUuid.v5(namespace, name)));
    }
}

这是输出结果:

UUID.nameUUIDFromBytes():     '9e120341-627f-32be-8393-58b5d655b751'

HashUuid.v3(name):            '9e120341-627f-32be-8393-58b5d655b751'
HashUuid.v5(name):            'e4586bed-032a-5ae6-9883-331cd94c4ffa'
HashUuid.v3(namespace, name): 'f0043437-723b-308f-a6c0-74ec36ddf9c2'
HashUuid.v5(namespace, name): '18a45fd8-8fab-5647-aad7-1d3264932180'

或者,您也可以使用uuid-creator。请参考以下示例:

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

在生成UUID时,为什么您认为应该优先选择SHA1而不是MD5? - Zhro
我认为它并不总是应该被优先考虑。这取决于具体情况。RFC-4122在其第4.3节中指出,如果不存在向后兼容性问题,则首选SHA-1。我会修正我的评论。谢谢。 - fabiolimace

7

建议使用UUID v5

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

UUID v5需要一个命名空间。该命名空间应该是一个UUID v4,你可以在线生成它。在这里生成。命名空间确保了对于给定的输入,输出将始终相同。

UUID v5的一个可能的实现可以在这里找到

<!-- 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);
< p > < em >(某种程度上,命名空间的作用类似于种子对于随机数生成器的作用。相比之下,虽然种子应该是随机的,但我们的命名空间是一个常量。这迫使我们的生成器始终为给定的输入产生相同的值。)

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

谢谢,我正在寻找一种 JavaScript 的替代方案 :) - Sanidhya Gaur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接