什么是有效的UUID?

6

我生成 UUID,并在代码中使用正则表达式进行有效性验证;我遇到了令我困惑的问题

这是生成 UUID 的代码(在 mongodb 上下文中):

import java.util.UUID;
... ...

Document setOnInsert = new Document(Params.sender, UUID.randomUUID())
                                    .append(Params.userDevice, userDevice)
                                    .append(Params.hostId,"");

这是一个验证UUID的代码;我从这个帖子中复制了正则表达式。
static final Pattern UUID = Pattern.compile("([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})");

    public static boolean isUUID(String uuid){
        if(uuid == null){
            return false;
        }else{
            return UUID.matcher(uuid).matches();
        }
    }

以下是我遇到问题的两个UUID。
aa4aaa2c-c6ca-d5f5-b8b2-0b5c78ee2cb7
b24dd64c-de6b-5bf6-6283-aa2167cc93a7

这两个UUID是由上述代码生成的;在我的最新调试中,验证方法(isUUID())判断它们无效;但我将这些UUID发布到验证器在线进行验证,它说是可以的。
这是我的系统信息。
wjz@bj:~$ java -version 
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
wjz@bj:~$ 
wjz@bj:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.1 LTS
Release:    16.04
Codename:   xenial
wjz@bj:~$ 

背景:我之前一直在使用jdk 1.8.0_111,那时生成的UUID没有问题。但今天我升级到了1.8.0_121,然后遇到了这个问题...

所以我的问题是:上述提到的UUID是正确的还是错误的?应该相信生成器还是验证器?


5
为什么不直接使用UUID类来验证它们?UUID.fromString() - wvdz
6
问题出在这个模式的一部分上: [1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}。第一个字母应该是1-5而不是"d",第二个字母应该是8-b而不是6。 - Jon Skeet
6
RFC 4122对这些组的第一个十六进制数字没有限制,它只说明该组由两个hexOctet(每个都是两个十六进制数字)组成。您比规范更加限制了它。请信任内置生成器,不要相信“在互联网上找到”的验证器。 - Amadan
实际上,isUUID() 是我代码中在使用 UUID.fromString() 之前的一种保护措施;否则,如果我的字符串输入不是 UUID,我可能会遇到异常。 - George Wang
可能是Foolproof way of differentiating String and UUID的重复问题。 - Victor
显示剩余2条评论
4个回答

8

我的建议是不要重复造轮子。

基本上,如果您使用UUID.randomUUID()生成ID,则无需验证它们。如果您仍然好奇它们是否会被手动操纵,您可以使用UUID.fromString(yourUUID)并捕获可能抛出的IllegalArgumentExcepetionNumberFormatException

抛出IllegalArgumentExcepetion

如果名称不符合toString()中描述的字符串表示形式

此外,您可以在后台检查UUID是否已正确转换:

UUID id = UUID.fromString(yourUUID);
if(id.toString().equals(yourUUID){
    //success
}

1
在Java 8中,如果UUID无效,fromString不会抛出异常。它只是简单地截取一些数字并认为UUID有效。这是一个已知的错误,请参见https://bugs.java.com/view_bug.do?bug_id=8159339。正如@JacksOnF1re所提到的,equals方法可以完成工作。 - ampofila
1
需要注意的一件事是,UUID.fromString没有指定预期的UUID版本,因此它会猜测并采用任何有效的UUID。因此,您还可以使用yourUUID.version() == 4来检查版本。 - Christophe Roussy

4
您可以使用UUID.randomUUID()生成一个有效的UUID,无需使用正则表达式。

感谢给予我有关生成器的信心。情景是UUID已经生成,尚未被消耗并立即过期;相反,它们可能会存在相当长的时间,可以被保存、复制和传输。因此,在使用之前采取预防措施以避免任何异常。 - George Wang
@Jobin:我们没有看到 isUUID 被调用的地方,而且问题中已经包含了 UUID.randomUUID()。正则表达式只是用来验证输入的,所以使用它是可以的。 - Roland
2
我认为他不需要验证使用UUID.randomUUID()生成的UUID(将返回有效的UUID)。 - Jobin

1
有几个方面需要考虑:
  • UUID的总长度
  • 使用的字符
  • 组内有效大小(在短横线之间)
  • 小写或大写
  • 有效版本
  • 有效变体
  • 是否允许为空
要了解代码示例,请参见我最近添加到Apache Commons Validator的UUID验证器实现。它尚未合并,但您可以在此处投票支持:https://github.com/apache/commons-validator/pull/68

优秀的验证器! - fabiolimace
1
非常感谢@fabiolimace!我还向Hibernate Validators添加了一个bean验证约束。他们已经审核过了,但合并需要时间。如果您想支持它,请投票:https://github.com/hibernate/hibernate-validator/pull/1199 - Daniel Heid

1

我查看了一些其他的SO答案,包括其他语言......,这里提供一个纯Java解决方案,可以处理v4情况(在Java 8中默认使用):

 UUID myUuid = UUID.fromString(uuidStr); // Step 1, throws errors !
 ... myUuid.version() == 4 // Step 2, check for the version you desire

参见:https://docs.oracle.com/javase/9/docs/api/java/util/UUID.html#version--

这是一个完全有效的UUID字符串示例,但版本为1:

  public static void main(final String[] args) {
    final UUID myUuid = UUID.fromString("61614667-d279-11e7-a5ac-f941ac8dfc39");
    System.out.println(myUuid.version()); // Prints 1, not 4 !
  }

要尝试更多的v4 UUID,请使用UUID.randomUUID()或在线工具:https://www.uuidgenerator.net/version4 注意:JavaUUID.randomUUID()可以很好地工作,详见How good is Java's UUID.randomUUID? 安全提示:
静态工厂可用于检索类型4(伪随机生成的)UUID。UUID是使用密码学强的伪随机数生成器生成的。

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