我正在尝试手动生成一个小哈希来混淆我的webapp ID
(我已经解释了为什么我这样做而不是在这个帖子中使用像SHA,MDA这样的实际哈希,或者甚至像hashid这样的编码哈希:hashids vs pure random hash in id obfuscation)
这个哈希是纯随机的:我从字母数字字符串中选择一些数字:
"abcdefghijklmnopqrstuvwxyz1234567890"
这是我的代码:
model.rb
class Model< ApplicationRecord
before_save :set_hashid
validates :hashed_id, uniqueness: true
private
def set_hashid
chain = "abcdefghijklmnopqrstuvwxyz1234567890"
numberdigits = 4
until @hash.present? && !Model.exists?(hashed_id: @hash)
@hash = ""
numberdigits.times do
@hash << chain[rand(0..chain.size-1)]
end
end
self.hashed_id = @hash
end
end
效果非常好。创建由chain
字符串中的4位数组成的散列。它给出了36 ^ 4 = 1.679.616种可能性
我的模型很丰富(最终应达到1.000.000)
当字段hashed_id
中的模型搜索返回false时,哈希值会传递。
虽然有两个问题:
1st:我的数据库是空的,所以它非常快。但是当此列有1百万条记录时,索引列中4位数哈希的搜索时间是多少?有没有办法预测到这一点(试图找到一个我可能会努力的类似的虚拟表)
第2代:我使用uniqueness
强制执行验证,因此我想在我的set_hashid
内部搜索此哈希值,但在执行冗余验证时也会执行此操作。虽然我热衷于保持验证,以防一个类似的哈希在同一时刻与另一个用户通过测试(非常非常不可能,但谁知道)。有没有办法做不同的事情,所以不进行额外的搜索并保持验证?
答案 0 :(得分:1)
require 'secure_random'
class Model < ApplicationRecord
before_validation :set_hashid!, unless: -> { self.hash_id }
def set_hashid!
begin
self.hashed_id = SecureRandom.hex(2)
end while self.class.exists?(hashed_id: self.hashed_id)
end
end
begin ... while condtion
至少运行一次该块,并在碰撞发生时反复运行self.hashed_id = SecureRandom.hex(2)
。
2
是SecureRandom.hex
使用的位长(它的长度为4)。
唯一性验证不会提供任何额外的值,因为它只检查具有该值的记录是否存在,并且您已经在回调中检查了该值。应用程序级别验证也很容易出现您试图反击的竞争条件。
相反,这应该由数据库中的唯一索引强制执行。
您还可以创建Postgres函数并使用它生成随机字符串并在触发器中使用它来设置值。但我会从这开始,当性能实际上是一个问题时,请研究一下。