我想在我的控制器中为“user_info_token”列中的用户生成一个令牌。但是,我想检查当前没有用户拥有该令牌。这段代码是否足够?
begin
@new_token = SecureRandom.urlsafe_base64
user = User.find_by_user_info_token(@new_token)
end while user != nil
@seller.user_info_token = @new_token
或者有更清洁的方法吗?
答案 0 :(得分:50)
如果您的令牌足够长并且由加密安全[伪]随机数生成器生成,那么<strong>不需要验证令牌是否唯一。您不需要在循环中生成令牌。
16个原始源字节足够长,可以有效保证。格式化URL安全性时,结果会更长。
# Base-64 (url-safe) encoded bytes, 22 characters long
SecureRandom.urlsafe_base64(16)
# Base-36 encoded bytes, naturally url-safe, ~25 characters long
SecureRandom.hex(16).to_i(16).to_s(36)
# Base-16 encoded bytes, naturally url-safe, 32 characters long
SecureRandom.hex(16)
这是因为16字节或128位令牌不唯一的概率非常小,几乎为零。在生成约2 64 = 18,446,744,073,709,551,616 = 1.845 x 10 19 标记后,只有50% chance有任何重复。如果你开始每秒产生10亿个令牌,那么大约需要2个 64 /(10 9 * 3600 * 24 * 365.25)= 600 年直到有50%的可能性发生任何重复。
但是你不会每秒产生10亿个代币。让我们慷慨,假设你每秒产生一个令牌。即使一次碰撞有50%的可能性达到6000亿年的时间范围。在此之前,太阳已经被太阳吞噬了。
答案 1 :(得分:33)
我发现最干净的解决方案:
@seller.user_info_token = loop do
token = SecureRandom.urlsafe_base64
break token unless User.exists?(user_info_token: token)
end
还有一些非常干净但有潜在重复的东西(尽管很少):
@seller.user_info_token = SecureRandom.uuid
Random UUID probability of duplicates
编辑:当然,为您的:user_info_token
添加唯一索引。搜索具有相同令牌和的用户会更快,如果偶然的话,会在相同的时刻使用完全相同的令牌保存2个用户!
答案 2 :(得分:4)
我有很多模特我使用独特的令牌。出于这个原因,我在Tokened
app/models/concerns/tokened.rb
个问题
module Tokened
extend ActiveSupport::Concern
included do
after_initialize do
self.token = generate_token if self.token.blank?
end
end
private
def generate_token
loop do
key = SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
break key unless self.class.find_by(token: key)
end
end
end
在任何模型中我都想拥有独特的令牌,我只是做
include Tokened
但是,你的代码看起来也很好。
答案 3 :(得分:1)
也许你可以用实际时间做点什么。然后,您无需检查用户是否已使用该令牌。
new_token = Digest::MD5.hexdigest(Time.now.to_i.to_s + rand(999999999).to_s)
user.user_info_token = new_token
答案 4 :(得分:1)
Rails 5附带此功能,您只需在下一行添加到您的模型中:
class User
has_secure_token
end
由于Rails 5尚未发布,您可以使用has_secure_token gem。您还可以查看我的博文,了解有关它的更多信息https://coderwall.com/p/kb97gg/secure-tokens-from-rails-5-to-rails-4-x-and-3-x
答案 5 :(得分:0)
您可以尝试使用以下技巧来获取唯一令牌,这在我的项目中使用起来非常简单 -
CREDIT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def create_credit_key(count = 25)
credit_key = ""
key = CREDIT_CHARS.length
for i in 1..count
rand = Random.rand((0.0)..(1.0))
credit_key += CREDIT_CHARS[(key*rand).to_i].to_s
end
return credit_key
end
使用摘要再次更容易,我尝试在不使用任何算法的情况下生成。