Najlepszy sposób na stworzenie unikalnego tokena w Rails?

Oto, czego używam. Token niekoniecznie musi być słyszalny, aby się domyślić, jest to bardziej krótki identyfikator url niż cokolwiek innego i chcę go krótko trzymać. Skorzystałem z kilku przykładów, które znalazłem w Internecie i w razie kolizji, myślę, że poniższy kod odtworzy token, ale nie jestem pewien. Jestem jednak ciekaw lepszych sugestii, ponieważ wydaje się to trochę szorstkie na krawędziach.

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

Moja kolumna bazy danych dla tokena jest unikalnym indeksem i używam również validates_uniqueness_of :token na modelu, ale ponieważ są one tworzone w partiach automatycznie na podstawie działań użytkownika w aplikacji (składają zamówienie i kupują tokeny, zasadniczo), nie jest możliwe, aby aplikacja wyrzuciła błąd.

Mógłbym też, jak sądzę, zmniejszyć ryzyko kolizji, dodać kolejny ciąg na końcu, coś generowanego na podstawie czasu lub coś w tym stylu, ale nie chcę, aby token był zbyt długi.

Author: Sachin Gevariya, 2011-05-16

10 answers

-- Update --

Stan na 9 stycznia 2015. Rozwiązanie jest teraz zaimplementowane w Rails 5 ActiveRecord ' s secure token implementation .

-- szyny 4 & 3 --

Tylko dla przyszłego odniesienia, tworzenie bezpiecznego tokenu losowego i zapewnienie jego unikalności dla modelu (przy użyciu Ruby 1.9 i 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 zasugerował, a ja zgodziłem się zastąpić begin...end..while loop do...break unless...end w tym odpowiedź, ponieważ poprzednia implementacja może zostać usunięta w przyszłości.

Edytuj 2:

W przypadku Rails 4 i concerns, polecam przeniesienie tego do concern.

# 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:02

Ryan Bates używa ładnego kodu w swoim Railscast na zaproszeniach beta . Tworzy to 40-znakowy ciąg alfanumeryczny.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-03 03:24:57

Jest kilka całkiem sprytnych sposobów, aby to zrobić, pokazanych w tym artykule:

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

Moja ulubiona lista to:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-04 21:23:00

Może to być późna odpowiedź, ale aby uniknąć użycia pętli, można również wywołać metodę rekurencyjnie. Wygląda i czuje się nieco czystszy dla mnie.

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-03 13:33:13

Jeśli chcesz czegoś, co będzie wyjątkowe, możesz użyć czegoś takiego:

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

Wygeneruje to jednak ciąg 32 znaków.

Jest jednak inny sposób:

require 'base64'

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

Na przykład dla id jak 10000, wygenerowany token będzie jak "MTAwMDA=" (i można łatwo dekodować go dla id, po prostu zrobić

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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-16 18:18:31

To może być pomocne:

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

Jeśli chcesz usunąć dowolny znak specjalny niż umieścić w pierwszym argumencie '+ / = 'i każdy znak umieścić w drugim argumencie' 0aZ ' i 15 jest długość tutaj .

I jeśli chcesz usunąć dodatkowe spacje i znak nowej linii, dodaj takie rzeczy jak:

SecureRandom.base64(15).tr('+/=', '0aZ').strip.delete("\n")
Mam nadzieję, że to komuś pomoże.
 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/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-12 09:38:22

Spróbuj w ten sposób:

Od wersji Ruby 1.9, generowanie uuid jest wbudowane. Użyj funkcji SecureRandom.uuid.
generowanie GUID w Ruby

To było dla mnie pomocne

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:14

Możesz user has_secure_token https://github.com/robertomiranda/has_secure_token

Jest naprawdę prosty w użyciu

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-04 15:51:29

Aby utworzyć właściwy, mysql, varchar 32 GUID

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/doraprojects.net/template/agent.layouts/content.php on line 54
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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-08 16:36:27