使用Rails动态生成唯一令牌

Ala*_*man 19 ruby ruby-on-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 
Run Code Online (Sandbox Code Playgroud)

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

yfe*_*lum 51

如果您的令牌是足够长的时间,并通过加密的安全[伪]随机数生成器生成的,那么你就不会需要验证令牌是独一无二的.你不会需要产生一个循环的令牌.

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)
Run Code Online (Sandbox Code Playgroud)

这是因为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亿年的时间框架.在此之前,太阳已经被太阳吞噬了.

  • +1这应该是接受的答案.它是正确的,优雅的和简单的 - 并且具有逻辑实用性的支持. (4认同)

Dam*_*ien 33

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

@seller.user_info_token = loop do
  token = SecureRandom.urlsafe_base64
  break token unless User.exists?(user_info_token: token)
end
Run Code Online (Sandbox Code Playgroud)

而且非常干净但有可能重复的东西(尽管很少):

@seller.user_info_token = SecureRandom.uuid
Run Code Online (Sandbox Code Playgroud)

随机UUID重复概率

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