¿La mejor forma de crear un token único en Rails?

Esto es lo que estoy usando. El token no tiene necesariamente que ser escuchado para adivinar, es más como un identificador de url corto que cualquier otra cosa, y quiero que sea breve. He seguido algunos ejemplos que he encontrado en línea y en el caso de una colisión, creo que el código siguiente recreará el token, pero no estoy muy seguro. Tengo curiosidad por ver mejores sugerencias, sin embargo, ya que esto se siente un poco difícil en los bordes.

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 

Mi columna de base de datos para el token es un índice único y también estoy usando validates_uniqueness_of :token en el modelo, pero porque estos se crean automáticamente en lotes en función de las acciones de un usuario en la aplicación (hacen un pedido y compran los tokens, esencialmente ), no es factible que la aplicación arroje un error.

También podría, supongo, reducir la posibilidad de colisiones, agregar otra cadena al final, algo generado en función del tiempo o algo así, pero no quiero que el token sea demasiado largo.

– Actualización –

A partir del 9 de enero de 2015, la solución ahora se implementa en la implementación de token seguro de Rails 5 ActiveRecord .

– Carriles 4 y 3 –

Solo para referencia futura, crear un token aleatorio seguro y garantizar su exclusividad para el modelo (cuando se usa Ruby 1.9 y 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 

Editar:

@kain sugirió, y acepté, reemplazar el begin...end..while loop do...break unless...end en esta respuesta porque la implementación anterior podría eliminarse en el futuro.

Editar 2:

Con Rails 4 y las preocupaciones, recomendaría mover esto a la preocupación.

 # 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 

Ryan Bates usa un poco de código en sus invitaciones de Railscast . Esto produce una cadena alfanumérica de 40 caracteres.

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

Hay algunas maneras bastante hábiles de hacer esto demostrado en este artículo:

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

Mi lista favorita es esta:

 rand(36**8).to_s(36) => "uur0cj2h" 

Esta podría ser una respuesta tardía, pero para evitar el uso de un bucle también puede llamar al método recursivamente. Se ve y se siente un poco más limpio para mí.

 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 

Si quieres algo que sea único, puedes usar algo como esto:

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

sin embargo, esto generará una cadena de 32 caracteres.

Sin embargo, hay otra forma:

 require 'base64' def after_create update_attributes!(:token => Base64::encode64(id.to_s)) end 

por ejemplo, para id como 10000, el token generado sería como “MTAwMDA =” (y usted puede decodificarlo fácilmente para el id, simplemente haga

 Base64::decode64(string) 

Esto puede ser útil:

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

Si desea eliminar cualquier carácter especial, coloque el primer argumento ‘+ / =’ y cualquier carácter puesto en el segundo argumento ‘0aZ’ y 15 es la longitud aquí.

Y si desea eliminar los espacios adicionales y el nuevo carácter de línea, agregue cosas como:

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

Espero que esto ayude a cualquiera.

Pruebe de esta manera:

A partir de Ruby 1.9, la generación de uuid está incorporada. Use la función SecureRandom.uuid .
Generando Guids en Ruby

Esto fue útil para mí

puedes usar has_secure_token https://github.com/robertomiranda/has_secure_token

es realmente simple de usar

 class User has_secure_token :token1, :token2 end user = User.create user.token1 => "44539a6a59835a4ee9d7b112b48cd76e" user.token2 => "226dd46af6be78953bde1641622497a8" 

Para crear un GUID apropiado, mysql, varchar 32

 SecureRandom.uuid.gsub('-','').upcase 
 def generate_token self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--") end