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.
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
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)
il y a des façons assez astucieuses de le faire démontré dans cet article:
Ma liste préférée est celle-ci:
rand(36**8).to_s(36)
=> "uur0cj2h"
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
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)
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.
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
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"
Pour créer un bon, mysql, varchar 32 GUID
SecureRandom.uuid.gsub('-','').upcase
def generate_token
self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end