在Rails中创建唯一令牌的最佳方法?

Sli*_*k23 154 random guid ruby-on-rails ruby-on-rails-3

这就是我正在使用的.令牌不一定要被猜测,它更像是一个短网址标识符,而不是其他任何东西,我想保持简短.我已经按照我在网上找到的一些例子,如果发生碰撞,我认为下面的代码会重新创建令牌,但我不确定.不过,我很想看到更好的建议,因为边缘感觉有点粗糙.

def self.create_token
    random_number = SecureRandom.hex(3)
    "1X#{random_number}"

    while Tracker.find_by_token("1X#{random_number}") != nil
      random_number = SecureRandom.hex(3)
      "1X#{random_number}"
    end
    "1X#{random_number}"
  end
Run Code Online (Sandbox Code Playgroud)

我的令牌的数据库列是一个唯一的索引,我也在validates_uniqueness_of :token模型上使用,但因为这些是根据用户在应用程序中的操作自动批量创建的(他们下订单并购买令牌,基本上),它是让应用程序抛出错误是不可行的.

我猜,我也可以减少碰撞的几率,在最后添加另一个字符串,根据时间或类似的东西生成的东西,但我不希望令牌太长.

Kru*_*ule 328

- 更新 -

截至2015年1月9日.该解决方案现在在Rails 5 ActiveRecord的安全令牌实现中实现.

- Rails 4和3 -

仅供将来参考,创建安全随机令牌并确保其对模型的唯一性(使用Ruby 1.9和ActiveRecord时):

class ModelName < ActiveRecord::Base

  before_create :generate_token

  protected

  def generate_token
    self.token = loop do
      random_token = SecureRandom.urlsafe_base64(nil, false)
      break random_token unless ModelName.exists?(token: random_token)
    end
  end

end
Run Code Online (Sandbox Code Playgroud)

编辑:

@kain建议,我同意,在这个答案中替换begin...end..while,loop do...break unless...end因为以前的实现可能会在将来被删除.

编辑2:

有了Rails 4和关注点,我建议关注它.

# app/models/model_name.rb
class ModelName < ActiveRecord::Base
  include Tokenable
end

# app/models/concerns/tokenable.rb
module Tokenable
  extend ActiveSupport::Concern

  included do
    before_create :generate_token
  end

  protected

  def generate_token
    self.token = loop do
      random_token = SecureRandom.urlsafe_base64(nil, false)
      break random_token unless self.class.exists?(token: random_token)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 这个确切的代码不起作用,因为random_token在循环中作用域. (7认同)

Nat*_*ird 51

Ryan Bates在他的Railscast中使用了一些不错的代码来发布测试版邀请函.这将生成一个40个字符的字母数字字符串.

Digest::SHA1.hexdigest([Time.now, rand].join)
Run Code Online (Sandbox Code Playgroud)

  • @ Slick23你也可以随时抓取一部分字符串:`Digest :: SHA1.hexdigest([Time.now,rand] .join)[0..10]` (12认同)
  • 是的,那不错.我通常会寻找更短的字符串,作为URL的一部分使用. (3认同)

cor*_*ard 30

在本文中演示了一些非常流畅的方法:

https://web.archive.org/web/20121026000606/http://blog.logeek.fr/2009/7/2/creating-small-unique-tokens-in-ruby

我最喜欢的是这个:

rand(36**8).to_s(36)
=> "uur0cj2h"
Run Code Online (Sandbox Code Playgroud)

  • 这篇博客文章的作者!是的:在这种情况下,我总是添加一个db约束或类似的断言unicity. (6认同)
  • 您将始终需要对现有令牌进行额外检查.我没有意识到这并不明显.只需添加`validates_uniqueness_of:token`并通过迁移向表中添加唯一索引. (4认同)

Mar*_*Pop 30

这可能是一个迟到的响应,但为了避免使用循环,您也可以递归调用该方法.它看起来和感觉稍微清洁一点.

class ModelName < ActiveRecord::Base

  before_create :generate_token

  protected

  def generate_token
    self.token = SecureRandom.urlsafe_base64
    generate_token if ModelName.exists?(token: self.token)
  end

end
Run Code Online (Sandbox Code Playgroud)


Ess*_*sse 17

如果你想要一些独特的东西,你可以使用这样的东西:

string = (Digest::MD5.hexdigest "#{ActiveSupport::SecureRandom.hex(10)}-#{DateTime.now.to_s}")
Run Code Online (Sandbox Code Playgroud)

但是这将生成32个字符的字符串.

然而,还有其他方式:

require 'base64'

def after_create
update_attributes!(:token => Base64::encode64(id.to_s))
end
Run Code Online (Sandbox Code Playgroud)

例如对于像10000这样的id,生成的令牌就像"MTAwMDA ="(并且您可以轻松地将其解码为id,只需制作

Base64::decode64(string)
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,`Base64 :: encode64(id.to_s)`违背了使用令牌的目的.您很可能使用令牌来隐藏ID,并使任何没有令牌的人无法访问该资源.但是,在这种情况下,有人可以只运行`Base64 :: encode64(<insert_id_here>)`,他们会立即获得您网站上每个资源的所有令牌. (11认同)

Vik*_*Vik 14

这可能会有所帮助:

SecureRandom.base64(15).tr('+/=', '0aZ')
Run Code Online (Sandbox Code Playgroud)

如果你想删除任何特殊字符而不是放在第一个参数'+/='中,并且任何字符放在第二个参数'0aZ'中,15就是这里的长度.

如果你想删除额外的空格和换行符而不是添加以下内容:

SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")
Run Code Online (Sandbox Code Playgroud)

希望这对任何人都有帮助.

  • ````SecureRandom.urlsafe_base64```也实现了同样的目标. (16认同)
  • 如果你不想要像"+/="这样奇怪的字符,你可以使用SecureRandom.hex(10)而不是base64. (3认同)

Nic*_*nko 7

试试这种方式:

从Ruby 1.9开始,uuid生成就是内置的.使用该SecureRandom.uuid功能.
在Ruby中生成Guids

这对我很有帮助


use*_*938 6

你可以用户has_secure_token https://github.com/robertomiranda/has_secure_token

使用起来非常简单

class User
  has_secure_token :token1, :token2
end

user = User.create
user.token1 => "44539a6a59835a4ee9d7b112b48cd76e"
user.token2 => "226dd46af6be78953bde1641622497a8"
Run Code Online (Sandbox Code Playgroud)

  • @AdrianMatteo我有同样的问题.根据我的理解,`has_secure_token`附带Rails 5,但我使用的是4.x. 我已按照[本文](https://coderwall.com/p/kb97gg/secure-tokens-from-rails-5-to-rails-4-x-and-3-x)上的步骤进行操作适合我. (3认同)

Aar*_*son 5

创建适当的mysql varchar 32 GUID

SecureRandom.uuid.gsub('-','').upcase
Run Code Online (Sandbox Code Playgroud)