Il modo migliore per creare un token unico in Rails?

Ecco cosa sto usando. Il token non deve necessariamente essere sentito per indovinare, è più simile a un identificatore di url breve di qualsiasi altra cosa, e voglio tenerlo breve. Ho seguito alcuni esempi che ho trovato online e in caso di collisione, penso che il codice seguente ricreerà il token, ma non ne sono sicuro. Sono curioso di vedere suggerimenti migliori, però, dato che mi sembra un po ‘approssimativo ai bordi.

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 

La mia colonna di database per il token è un indice univoco e sto anche usando validates_uniqueness_of :token sul modello, ma poiché questi sono creati automaticamente in batch in base alle azioni dell’utente nell’app (effettuano un ordine e acquistano i token, in sostanza ), non è ansible che l’app abbia generato un errore.

Potrei anche, credo, ridurre la possibilità di collisioni, aggiungere un’altra stringa alla fine, qualcosa generato in base al tempo o qualcosa del genere, ma non voglio che il token diventi troppo lungo.

— Aggiornare —

A partire dal 9 gennaio 2015, la soluzione è ora implementata nell’implementazione di token sicuro di Rails 5 ActiveRecord .

– Rotaie 4 e 3 –

Solo per riferimento futuro, creando token casuali sicuri e assicurandone l’unicità per il modello (quando si utilizza Ruby 1.9 e 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 

Modificare:

@kain ha suggerito e ho accettato di sostituire begin...end..while con loop do...break unless...end in questa risposta perché l'implementazione precedente potrebbe essere rimossa in futuro.

Modifica 2:

Con Rails 4 e preoccupazioni, raccomanderei di spostare questo problema.

 # 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 bel po ‘di codice nel suo Railscast sugli inviti alla beta . Questo produce una stringa alfanumerica di 40 caratteri.

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

Ci sono alcuni modi piuttosto chiari di fare questo dimostrato in questo articolo:

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

La mia lista preferita è questa:

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

Potrebbe trattarsi di una risposta tardiva, ma per evitare di utilizzare un ciclo è ansible chiamare il metodo in modo ricorsivo. Sembra e mi sembra leggermente più pulito.

 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 

Se vuoi qualcosa che sarà unico puoi usare qualcosa del genere:

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

tuttavia questo genererà una stringa di 32 caratteri.

C’è comunque un altro modo:

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

per esempio per id come 10000, il token generato sarebbe come “MTAwMDA =” (e puoi facilmente decodificarlo per id, basta fare

 Base64::decode64(string) 

Questo può essere utile:

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

Se si desidera rimuovere un carattere speciale anziché inserire il primo argomento ‘+ / =’ e qualsiasi carattere inserito nel secondo argomento ‘0aZ’ e 15 è la lunghezza qui.

E se vuoi rimuovere gli spazi extra e il carattere della nuova riga piuttosto che aggiungere cose come:

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

Spero che questo possa aiutare nessuno.

Prova in questo modo:

A partire da Ruby 1.9, la generazione di uuid è integrata. Usa la funzione SecureRandom.uuid .
Generazione di Guids in Ruby

Questo è stato utile per me

puoi utente has_secure_token https://github.com/robertomiranda/has_secure_token

è veramente semplice da usare

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

Per creare un GUID vqarhar 32 corretto, mysql

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