从相同的字符串生成相同的UUID

3

我希望从一个随机字符串生成UUID字符串,以便相同的输入字符串生成相同的UUID。

我不关心从UUID获取输入字符串。我需要这样做是为了确定性地转换数据库中的键作为迁移的一部分,以便不同的客户端并行操作会收敛到相同的结果。

这个帖子中的接受答案提供了Java版本的答案,我需要Swift版本。


1
字符串的长度有限制吗? - Rob Napier
2
@JoakimDanielson,我不明白你的评论有什么意义。难道不是 Stack Overflow 用来提问的吗?我尝试了不同的 NSUUID 方法,但没有找到任何解决方案。 - Morpheus
3
你可以阅读《如何提出好问题》(https://stackoverflow.com/help/how-to-ask),但最重要的是你应该展示出你解决问题的努力或者解释你的研究,否则这更像是一个请求而不是一个问题。 - Joakim Danielson
3
我不确定为什么这个问题引起了这么多的反对。它的问题很清楚,有一个明确但不是显而易见的答案,展示了研究成果,并且Java方法在Swift中没有直接的等价物(因此这不只是一个语言转换的问题)。 - Rob Napier
1
@Coconuts 如果你觉得某些评论不友好,那么你可以标记它们。我刚刚为这个页面上的一条评论这样做了。 - Bart van Kuik
显示剩余4条评论
3个回答

12

官方的方法是使用版本5的UUID (RFC 4122 第4.3节):

4.3 用于创建基于名称的 UUID 的算法

版本3或5的 UUID 用于从“名称”中生成 UUID,这些“名称”来自某个“命名空间”,并在该命名空间内唯一。

处理过程是将字符串散列化,然后将其插入到 UUID 中。我将严格遵循规范,但我会标记您可以忽略的部分,即使如此,它仍将正确工作。

正如 matt 提到的那样,如果你只需要哈希,你可以直接使用 SHA。但如果你的系统确实需要 UUID,则应按照以下方式操作。

  • 定义命名空间(这不是完全必要的,但它将确保您的 UUID 全局唯一):
let namespace = "com.example.mygreatsystem:"
  • 将其与您的字符串组合
let inputString = "arandomstring"
let fullString = namespace + inputString
  • 哈希值。UUID v5规范特别要求使用SHA-1,但是您可以在此处改用SHA-256(SHA-2)。在这里使用SHA-1实际上没有安全问题,但是尽可能使用SHA-2是一个好的做法。
import CryptoKit

let hash = Insecure.SHA1.hash(data: Data(fullString.utf8)) // SHA-1 by spec
或者
let hash = SHA256.hash(data: Data(fullString.utf8)) // SHA-2 is generally better
  • 取数据的前128位。(从SHA-1或SHA-2哈希中提取任意子集位均是安全的。每个位都“基本上是随机的”)
var truncatedHash = Array(hash.prefix(16))
  • 正确设置版本和变体位。对大多数用途来说,这并不真的很重要。我从未遇到过实际解析UUID元数据的系统。但它是规范的一部分。如果您将来使用v4随机UUID记录(这几乎是今天每个系统的“正常” UUID),那么这将允许您区分由哈希创建的UUID和随机生成的UUID,在任何情况下都具有重要性。请参见UUIDTools,了解格式的良好可视化介绍。
truncatedHash[6] &= 0x0F    // Clear version field
truncatedHash[6] |= 0x50    // Set version to 5

truncatedHash[8] &= 0x3F    // Clear variant field
truncatedHash[8] |= 0x80    // Set variant to DCE 1.1
  • 最后,计算您的UUID:
let uuidString = NSUUID(uuidBytes: truncatedHash).uuidString

1
太棒了!非常感谢。 - Morpheus

2
我希望从随机字符串生成UUID字符串,以便相同的输入字符串生成相同的UUID。
您正在描述哈希值,而不是UUID。 UUID中的U代表唯一性; 每次都会得到一个不同的UUID。例如,对于相同的字符串,SHA-1哈希相同,对于不同的字符串则不同。

哦,我明白了。SHA-1 会有冲突的风险吗?(即相同值但不同字符串) - Morpheus

0

@Nicolas BachschmidtUUIDKit 在 Swift 中实现了完整的 UUID v5。

它还支持 RFC 1808 中的所有命名空间,例如 NameSpace_URL

import UUIDKit
UUID.v5(name: "http://example.com/index.html", namespace: .url)

因为它遵循规范,使用SHA-1并且输出与其他平台实现相匹配,所以上述行的输出与此Python代码的输出值相同:

import uuid
uuid.uuid5(uuid.NAMESPACE_URL, 'http://example.com/index.html')

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