使用Rails动态生成唯一令牌

我想在我的控制器中为“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 

或者有更清洁的方法来做到这一点?

我发现最干净的解决方案:

 @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 

随机UUID重复概率

编辑:当然,为您的:user_info_token添加一个唯一索引。 搜索具有相同令牌的用户会快得多,如果偶然的话,会在同一时刻以完全相同的令牌保存2个用户这将引发exception!

如果您的令牌足够长并且由加密安全[伪]随机数生成器生成,那么您无需validation令牌是否唯一。 您不需要在循环中生成标记。

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%的可能性有重复。 如果你开始每秒产生10亿个代币,则需要大约2 64 /(10 9 * 3600 * 24 * 365.25)= 600 年,直到有50%的概率发生任何重复。

但是你每秒钟不会产生10亿个令牌。 让我们慷慨,假设你每秒产生一个令牌。 即使一次碰撞有50%的可能性达到6000亿年的时间框架。 在此之前,太阳已经被太阳吞噬了。

我有很多模型我使用独特的令牌。 出于这个原因,我在app/models/concerns/tokened.rb创建了一个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 

但是,是的,您的代码看起来也很好。

也许你可以用实际时间做点什么。 然后,您无需检查用户是否已使用该令牌。

 new_token = Digest::MD5.hexdigest(Time.now.to_i.to_s + rand(999999999).to_s) user.user_info_token = new_token 

Rails 5附带此function,您只需在下一行添加到您的模型:

 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

你可以尝试一些下面的技巧来获得唯一的令牌,它在我的项目中使用它很容易 –

 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 

使用摘要再次更容易,在这里我尝试不使用任何算法生成。