这是我正在使用的。不一定要听到令牌的猜测,它更像是一个简短的URL标识符而不是其他任何东西,我想保持简短。我已经按照我在网上找到的一些例子,如果发生碰撞,我认为下面的代码会重新创建令牌,但我不确定。不过,我很想看到更好的建议,因为边缘感觉有点粗糙。
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
令牌的我的数据库列是一个唯一索引,我也在模型上使用validates_uniqueness_of :token
,但因为这些是根据用户在应用程序中的操作自动批量创建的(他们下订单和从本质上来说,购买令牌,让应用程序抛出错误是不可行的。
我想,我也可以减少碰撞的几率,在最后附加另一个字符串,根据时间或类似的东西生成的东西,但我不希望令牌太长。
答案 0 :(得分:325)
- 更新 -
从 2015年1月9日开始。此解决方案现已在 Rails 5 ActiveRecord's secure token implementation中实施。
- 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
修改强>
@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
答案 1 :(得分:50)
Ryan Bates在Railscast on beta invitations中使用了一小段代码。这将生成一个40个字符的字母数字字符串。
Digest::SHA1.hexdigest([Time.now, rand].join)
答案 2 :(得分:30)
在本文中演示了一些非常流畅的方法:
我最喜欢的是:
rand(36**8).to_s(36)
=> "uur0cj2h"
答案 3 :(得分:28)
这可能是迟到的响应,但为了避免使用循环,您还可以递归调用该方法。它的外观和感觉对我来说都比较干净。
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
答案 4 :(得分:17)
如果你想要一些独特的东西,你可以使用这样的东西:
string = (Digest::MD5.hexdigest "#{ActiveSupport::SecureRandom.hex(10)}-#{DateTime.now.to_s}")
然而,这将生成32个字符的字符串。
还有其他方法:
require 'base64'
def after_create
update_attributes!(:token => Base64::encode64(id.to_s))
end
例如对于像10000这样的id,生成的令牌就像“MTAwMDA =”(并且您可以轻松地将其解码为id,只需制作
Base64::decode64(string)
答案 5 :(得分:14)
这可能会有所帮助:
SecureRandom.base64(15).tr('+/=', '0aZ')
如果你想删除任何特殊字符,而不是放在第一个参数'+ / ='中,并且任何字符放在第二个参数'0aZ'中,15是这里的长度。
如果你想删除多余的空格和换行符而不是添加以下内容:
SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")
希望这对任何人都有帮助。
答案 6 :(得分:7)
答案 7 :(得分: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"
答案 8 :(得分:5)
创建一个合适的mysql,varchar 32 GUID
SecureRandom.uuid.gsub('-','').upcase
答案 9 :(得分:1)
def generate_token
self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end
答案 10 :(得分:0)
我认为令牌应像密码一样处理。因此,它们应该在数据库中加密。
我正在做这样的事情来为模型生成一个唯一的新令牌:
CIRAWFilterOption(s)