La meilleure façon de créer un token unique dans les Rails?

voilà ce que j'utilise. Le token ne doit pas nécessairement être entendu pour deviner, c'est plus comme un identifiant d'url court que n'importe quoi d'autre, et je veux le garder court. J'ai suivi quelques exemples que j'ai trouvés en ligne et en cas de collision, je pense que le code ci-dessous va recréer le token, mais je ne suis pas vraiment sûr. Je suis curieux de voir de meilleures suggestions, bien que, comme cela se sent un peu rude sur les bords.

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

My la colonne de base de données pour le token est un index unique et j'utilise aussi validates_uniqueness_of :token sur le model, mais parce que ceux-ci sont créés en lots automatiquement basés sur les actions d'un utilisateur dans l'application (ils placent une commande et achètent les tokens, essentiellement), il n'est pas possible que l'application lance une erreur.

je pourrais aussi, je pense, réduire le risque de collisions, ajouter une autre chaîne à la fin, quelque chose généré basé sur le temps ou quelque chose comme ça, mais je ne veux pas de la jeton trop long.

146
demandé sur Sachin Gevariya 2011-05-16 21:57:22

10 réponses

-- Update --

en date du 9 janvier 2015. la solution est maintenant implémentée dans Rails 5 1519130920" Activerecord's secure token implementation .

-- Rails 4 & 3 --

juste pour référence future, en créant un jeton aléatoire sûr et en assurant son unicité pour le modèle (lors de L'utilisation de Ruby 1.9 et 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 a suggéré, et j'ai accepté, de remplacer begin...end..while par loop do...break unless...end dans cette réponse parce que la mise en œuvre antérieure pourrait être supprimée à l'avenir.

Edit 2:

avec les Rails 4 et les préoccupations, je recommande de déplacer cette question à préoccupation.

# 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
répondu Krule 2017-05-23 12:03:02

Ryan Bates utilise un petit peu de code dans son Railscast sur beta invitations . Cela produit une chaîne alphanumérique de 40 caractères.

Digest::SHA1.hexdigest([Time.now, rand].join)
46
répondu Nate Bird 2012-01-03 03:24:57

il y a des façons assez astucieuses de le faire démontré dans cet article:

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

Ma liste préférée est celle-ci:

rand(36**8).to_s(36)
=> "uur0cj2h"
27
répondu coreyward 2016-02-04 21:23:00

Cela pourrait être une réponse tardive, mais pour éviter à l'aide d'une boucle, vous pouvez également appeler la méthode récursive. Il ressemble et se sent un peu plus propre pour moi.

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
répondu Marius Pop 2014-04-03 13:33:13

Si vous voulez quelque chose qui sera unique vous pouvez utiliser quelque chose comme ceci:

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

cependant, cela va générer de la chaîne de 32 caractères.

il y a cependant une autre voie:

require 'base64'

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

par exemple pour id comme 10000, Le token généré serait comme "MTAwMDA=" (et vous pouvez facilement le décoder pour id, juste faire

Base64::decode64(string)
17
répondu Esse 2011-05-16 18:18:31

Cela peut être utile :

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

si vous voulez supprimer n'importe quel caractère spécial que mis dans le premier argument '+/=' et n'importe quel caractère mis dans le deuxième argument '0aZ' et 15 est la longueur ici .

et si vous voulez supprimer les espaces supplémentaires et le caractère de nouvelle ligne que d'ajouter les choses comme:

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

J'espère que cela aidera n'importe qui.

14
répondu Vik 2011-10-12 09:38:22

Essayer de cette façon:

à partir de Ruby 1.9, la génération uuid est intégrée. Utilisez la fonction SecureRandom.uuid .

Générer un Guid dans Ruby

ce fut utile pour moi

7
répondu Nickolay Kondratenko 2017-05-23 12:18:14

vous pouvez utiliser has_secure_token https://github.com/robertomiranda/has_secure_token

est vraiment simple à utiliser

class User
  has_secure_token :token1, :token2
end

user = User.create
user.token1 => "44539a6a59835a4ee9d7b112b48cd76e"
user.token2 => "226dd46af6be78953bde1641622497a8"
6
répondu user2627938 2014-12-04 15:51:29

Pour créer un bon, mysql, varchar 32 GUID

SecureRandom.uuid.gsub('-','').upcase
4
répondu Aaron Henderson 2013-09-04 08:58:50
def generate_token
    self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end
1
répondu miosser 2014-03-08 16:36:27