使用Rails动态生成唯一令牌

时间:2013-09-01 01:12:13

标签: ruby-on-rails ruby

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

或者有更清洁的方法吗?

6 个答案:

答案 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

使用摘要再次更容易,我尝试在不使用任何算法的情况下生成。