我计划使用SecureRandom.hex
为我的用户生成API密钥。
到目前为止,这里是3次执行的输出:
Loading development environment (Rails 5.2.1)
2.3.5 :001 > SecureRandom.hex
=> "0369e9b7c6ffa07bd8d0a263f7b4cfa6"
2.3.5 :002 > SecureRandom.hex
=> "1a8a168d7f70676451e3d59353e22693"
2.3.5 :003 > SecureRandom.hex
=> "94cc188e9e5c3abfe587510fa79993ce"
我得到重复结果的机会是什么?
我创建的这种方法会真正避免产生重复的内容吗?
def generate_string
string = SecureRandom.hex
generate_string if Model.where(:key => string).count > 0
string
end
unique_string = generate_string
我正在使用递归,如果该字符串已经存储在数据库中,它将只产生另一个。
而且,由于我没有得到重复,因此SecureRandom.hex
可以用多少个字符串才能用尽组合来产生?
答案 0 :(得分:1)
由于它是一个以16为基数的数字系统,因此存在16 ** SecureRandom.hex.length
可能的变化,而且很多。
如果您不想在用户数量增加时使堆栈溢出,则最好在退出条件下使用循环。
MAX_ATTEMPTS = 3 # For you to choose.
def key
MAX_ATTEMPTS.times do
hex = SecureRandom.hex
return hex unless Model.where(key: hex).exists?
end
fail 'No attempts to generate a key left.'
end
答案 1 :(得分:1)
在您的示例中(使用默认长度为32的SecureRandom.hex
时)
16**32 = 340282366920938463463374607431768211456
可能有不同的十六进制值。这意味着1:340282366920938463463374607431768211456
有机会创建一个副本。这次机会极低,恕我直言,不必为此担心太多。
当您要将密钥存储在数据库中时,我建议向该数据库列添加唯一索引,以确保在数据库级别上不可能存储任何重复项。
此外,您询问示例代码是否足以避免重复。答案是否。这是高度理论上的,因为可能性很低,但是您可能会遇到竞争情况,在这种情况下,两个作业同时生成相同的密钥,同时检查数据库中是否没有这样的密钥以及两个作业存储的密钥是否相同值插入表格。
重复的机会极低。数据库中key
列上的唯一索引可确保不会有任何重复(因为竞争条件或绕过此方法生成的键)。
答案 2 :(得分:1)
在您的设置中,键的长度似乎是32。
在三个执行中没有重复的机会是:
((16**32 - 1)/16**32) * ((16**32 - 2)/16**32)
因此,在三个执行中某处获得(至少一对)重复的机会是:
1 - ((16**32 - 1)/16**32) * ((16**32 - 2)/16**32)
= 8.8162076e-39
有:
16**32 = 3.4028237e+38
不同的字符串。
您的方法可以按预期工作时避免重复,但它也具有不可思议的微小可能性,使其永久消失并且永不终止。