Лучший способ создать уникальный токен в 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 на модели, но поскольку они создаются в пакетах автоматически на основе действий пользователя в приложении (они размещают заказ и покупают токены, по существу), невозможно, чтобы приложение выдавало ошибку.

я мог бы также, я думаю, уменьшить вероятность столкновений, добавить другую строку в конце, что-то сгенерированное на основе времени или что-то в этом роде, но я не хочу, чтобы токен получался слишком длинным.

10 ответов


-- Update --

по состоянию на 9 января, 2015. решение теперь реализовано в рельсы 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 в этом ответе, потому что предыдущая реализация может быть удалена в будущем.

Edit 2:

С рельсами 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

Райан Бейтс использует немного кода в своем Railscast на бета-приглашения. Это создает 40-символьную буквенно-цифровую строку.

Digest::SHA1.hexdigest([Time.now, rand].join)

есть несколько довольно скользких способов сделать это, продемонстрированных в этой статье:

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"

Это может быть поздний ответ, но чтобы избежать использования цикла, вы также можете вызвать метод рекурсивно. На мой взгляд, он выглядит и чувствует себя немного чище.

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

Если вы хотите что-то, что будет уникальным, вы можете использовать что-то вроде этого:

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)

Это может быть полезно :

SecureRandom.base64(15).tr('+/=', '0aZ')

Если вы хотите удалить какой-либо специальный символ, чем положить в первый аргумент '+/=' и любой символ, помещенный во второй аргумент '0aZ' и 15-это длина здесь .

и если вы хотите удалить лишние пробелы и символ новой строки, чем добавить такие вещи, как :

SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")

надеюсь, что это поможет кому-нибудь.


попробуйте так:

начиная с Ruby 1.9, поколение uuid встроено. Используйте .
создание GUID в Ruby

Это было полезно для меня


вы можете пользователь 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"

чтобы создать правильный, mysql, varchar 32 GUID

SecureRandom.uuid.gsub('-','').upcase

def generate_token
    self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end