Лучший способ создать уникальный токен в Rails?
Вот что я использую. Маркер не обязательно должен быть услышан, чтобы угадать, он скорее напоминает короткий идентификатор 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
для модели, но потому, что они создаются пакетами автоматически на основе действий пользователя в приложении (они размещают заказ и покупают по существу), не представляется возможным, чтобы приложение выбрасывало ошибку.
Я также мог бы уменьшить вероятность столкновений, добавить в конец другую строку, что-то созданное на основе времени или что-то в этом роде, но я не хочу, чтобы токен становился слишком длинным.
Ответы
Ответ 1
- Обновление -
По состоянию на 9 января 2015 года решение теперь реализовано в 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
Edit:
@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
Ответ 2
Райан Бэйтс использует в своем Railscast для бета-приглашений хороший код. Это создает буквенно-цифровую строку длиной 40 символов.
Digest::SHA1.hexdigest([Time.now, rand].join)
Ответ 3
Есть несколько хороших способов сделать это в этой статье:
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"
Ответ 4
Это может быть поздний ответ, но чтобы избежать использования цикла, вы также можете вызвать метод рекурсивно. Он выглядит и чувствует себя немного чище для меня.
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
Ответ 5
Если вы хотите что-то уникальное, вы можете использовать что-то вроде этого:
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
например, для id как 10000, сгенерированный токен будет похож на "MTAwMDA =" (и вы можете легко декодировать его для id, просто сделайте
Base64::decode64(string)
Ответ 6
Это может быть полезно:
SecureRandom.base64(15).tr('+/=', '0aZ')
Если вы хотите удалить какой-либо специальный символ, чем положить в первый аргумент '+/=', а любой символ, помещенный во второй аргумент '0aZ', и 15 - это длина здесь.
И если вы хотите удалить лишние пробелы и новый символ строки, добавьте такие вещи, как:
SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")
Надеюсь, это поможет кому угодно.
Ответ 7
Попробуйте следующим образом:
Начиная с Ruby 1.9, построена uuid. Используйте функцию SecureRandom.uuid
.
Создание гидов в Ruby
Это было полезно для меня
Ответ 8
вы можете использовать user_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"
Ответ 9
Чтобы создать правильный, mysql, varchar 32 GUID
SecureRandom.uuid.gsub('-','').upcase
Ответ 10
def generate_token
self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end
Ответ 11
Я думаю, что токен должен обрабатываться так же, как пароль. Как таковые, они должны быть зашифрованы в БД.
Я делаю что-то вроде этого, чтобы сгенерировать уникальный новый токен для модели:
key = ActiveSupport::KeyGenerator
.new(Devise.secret_key)
.generate_key("put some random or the name of the key")
loop do
raw = SecureRandom.urlsafe_base64(nil, false)
enc = OpenSSL::HMAC.hexdigest('SHA256', key, raw)
break [raw, enc] unless Model.exist?(token: enc)
end