La mejor manera de crear token único en Rails?


Esto es lo que estoy usando. El token no necesariamente tiene que ser escuchado para adivinar, es más como un identificador de url corto que cualquier otra cosa, y quiero mantenerlo corto. He seguido algunos ejemplos que he encontrado en línea y en el caso de una colisión, Creo el código a continuación recreará el token, pero no estoy muy seguro. Sin embargo, tengo curiosidad por ver mejores sugerencias, ya que esto se siente un poco áspero alrededor de 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 debido a que estos se crean en lotes automáticamente 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, para 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 se prolongue demasiado.

Author: Sachin Gevariya, 2011-05-16

10 answers

-- Actualizar {

A partir del 9 de enero de 2015. la solución ahora está implementada en Rails 5 Implementación de token seguro de ActiveRecord .

-- Rieles 4 & 3 --

Solo para referencia futura, creando token aleatorio seguro y asegurando su singularidad 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 yo estuve de acuerdo, reemplazar begin...end..while por loop do...break unless...end en este responder porque la implementación anterior podría eliminarse en el futuro.

Editar 2:

Con Rails 4 y preocupaciones, recomendaría mover esto a 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
 319
Author: Krule,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:02

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

Digest::SHA1.hexdigest([Time.now, rand].join)
 46
Author: Nate Bird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-03 03:24:57

Hay algunas formas bastante ingeniosas de hacer esto demostradas 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"
 27
Author: coreyward,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-04 21:23:00

Esto 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
 25
Author: Marius Pop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-03 13:33:13

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 manera:

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 puede decodificarlo fácilmente para id, simplemente haga

Base64::decode64(string)
 17
Author: Esse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-16 18:18:31

Esto puede ser útil :

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

Si desea eliminar cualquier carácter especial que poner en primer argumento '+/=' y cualquier carácter poner en segundo argumento '0aZ' y 15 es la longitud aquí .

Y si desea eliminar los espacios adicionales y el carácter de línea nueva que añadir las cosas como:

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

Espero que esto ayude a alguien.

 14
Author: Vik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-12 09:38:22

Intente de esta manera:

A partir de Ruby 1.9, la generación de uuid está integrada. Utilice la función SecureRandom.uuid.
Generar Guid en Ruby

Esto fue útil para mí

 7
Author: Nickolay Kondratenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:14

Usted puede user has_secure_token https://github.com/robertomiranda/has_secure_token

Es muy simple de usar

class User
  has_secure_token :token1, :token2
end

user = User.create
user.token1 => "44539a6a59835a4ee9d7b112b48cd76e"
user.token2 => "226dd46af6be78953bde1641622497a8"
 6
Author: user2627938,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-04 15:51:29

Para crear un GUID apropiado, mysql, varchar 32

SecureRandom.uuid.gsub('-','').upcase
 4
Author: Aaron Henderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-04 08:58:50
def generate_token
    self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end
 1
Author: miosser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-08 16:36:27