Voici ce que j'utilise. Le jeton ne doit pas forcément être entendu pour deviner, il ressemble plus à un identifiant d'URL abrégé qu'autre chose, 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 le code ci-dessous va recréer le jeton, mais je n'en suis pas vraiment sûr. Je suis curieux de voir de meilleures suggestions, cependant, car cela semble un peu rugueux 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
Ma colonne de base de données pour le jeton est un index unique et j'utilise également validates_uniqueness_of :token
sur le modèle, mais comme ceux-ci sont créés automatiquement par lots en fonction des actions de l'utilisateur dans l'application (essentiellement, il passe une commande et achète les jetons), il est impossible que l'application jette une erreur.
Je pourrais aussi, je suppose, pour réduire le risque de collision, ajouter une autre chaîne à la fin, quelque chose générée en fonction de l'heure ou quelque chose du genre, mais je ne veux pas que le jeton soit trop long.
-- Mettre à jour --
À compter du 9 janvier 2015} _, la solution est maintenant implémentée dans Rails 5Implémentation du jeton sécurisé d'ActiveRecord .
- Rails 4 & 3 -
Juste pour référence future, créez un jeton aléatoire sûr et assurez-vous que le modèle est unique (avec 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
Modifier:
@kain a suggéré, et j'ai accepté, de remplacer begin...end..while
par loop do...break unless...end
dans cette réponse car la mise en œuvre précédente pourrait être supprimée à l'avenir.
Edit 2:
Avec Rails 4 et les préoccupations, je recommanderais de déplacer cette 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 joli morceau de code dans son Railscast sur les invitations bêta . Cela produit une chaîne alphanumérique de 40 caractères.
Digest::SHA1.hexdigest([Time.now, Rand].join)
Cet article présente certaines méthodes assez astucieuses:
Ma liste préférée est la suivante:
Rand(36**8).to_s(36)
=> "uur0cj2h"
Cela peut être une réponse tardive, mais pour éviter d'utiliser une boucle, vous pouvez également appeler la méthode de façon récursive. Il semble et se sent légèrement 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 générera une chaîne de 32 caractères.
Il y a cependant un autre moyen:
require 'base64'
def after_create
update_attributes!(:token => Base64::encode64(id.to_s))
end
par exemple, pour un identifiant tel que 10000, le jeton généré serait du type "MTAwMDA =" (et vous pouvez facilement le décoder,
Base64::decode64(string)
Cela peut être utile:
SecureRandom.base64(15).tr('+/=', '0aZ')
Si vous souhaitez supprimer un caractère spécial, mettez le premier argument '+/=' et tout caractère inséré le second argument '0aZ' et 15 correspond à la longueur.
Et si vous souhaitez supprimer les espaces supplémentaires et le nouveau caractère de ligne, ajoutez les éléments suivants:
SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")
J'espère que cela aidera à tout le monde.
Essayez de cette façon:
A partir de Ruby 1.9, la génération uuid est intégrée. Utilisez la fonction SecureRandom.uuid
.
Génération de guides en Ruby
Cela m'a été utile
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 GUID correct, mysql, varchar 32
SecureRandom.uuid.gsub('-','').upcase
def generate_token
self.token = Digest::SHA1.hexdigest("--#{ BCrypt::Engine.generate_salt }--")
end
Je pense que le jeton devrait être traité comme un mot de passe. En tant que tels, ils doivent être chiffrés dans DB.
Je fais quelque chose comme ceci pour générer un nouveau jeton unique pour un modèle:
key = ActiveSupport::KeyGenerator
.new(Devise.secret_key)
.generate_key("put some random or the name of the key")
loop do
raw = SecureRandom.urlsafe_base64(nil, false)
enc = OpenSSL::HMAC.hexdigest('SHA256', key, raw)
break [raw, enc] unless Model.exist?(token: enc)
end