Создание уникального маркера на лету с Rails
Я хочу создать токен в моем контроллере для пользователя в столбце "user_info_token". Тем не менее, я хочу проверить, что в настоящее время у пользователя нет этого токена. Достаточно ли этого кода?
begin
@new_token = SecureRandom.urlsafe_base64
user = User.find_by_user_info_token(@new_token)
end while user != nil
@seller.user_info_token = @new_token
Или существует ли более чистый способ сделать это?
Ответы
Ответ 1
Самое чистое решение, которое я нашел:
@seller.user_info_token = loop do
token = SecureRandom.urlsafe_base64
break token unless User.exists?(user_info_token: token)
end
И что-то очень чистое, но с потенциальными дубликатами (очень немногие):
@seller.user_info_token = SecureRandom.uuid
Случайная вероятность дублирования UUID
Изменить: конечно, добавьте уникальный индекс в ваш :user_info_token
. Нам будет гораздо быстрее искать пользователя с тем же токеном и, если он по ошибке вызовет исключение, 2 пользователя будут сохранены в тот же момент с точно таким же токеном!
Ответ 2
Если ваш токен достаточно длинный и генерируется криптографически надежным генератором случайных чисел, тогда вы не должны убедиться, что токен уникален. Вы не должны генерировать токены в цикле.
16 исходных байтов достаточно долго для этой эффективной гарантии. При форматировании для безопасности URL результат будет длиннее.
# Base-64 (url-safe) encoded bytes, 22 characters long
SecureRandom.urlsafe_base64(16)
# Base-36 encoded bytes, naturally url-safe, ~25 characters long
SecureRandom.hex(16).to_i(16).to_s(36)
# Base-16 encoded bytes, naturally url-safe, 32 characters long
SecureRandom.hex(16)
Это связано с тем, что вероятность того, что 16-байтовый или 128-разрядный токен не является ununique, настолько исчезающе мала, что она практически равна нулю. Существует только 50% -ный шанс в случае повторений после приблизительно 2 64= 18 446 744 073 709 551 616 = 1,845 x 10 19. Если вы начнете генерировать один миллиард токенов в секунду, это займет примерно 2 64/(10 9 * 3600 * 24 * 365,25) = 600 лет, пока не будет 50% вероятность того, что произошли какие-либо повторения вообще.
Но вы не генерируете один миллиард токенов в секунду. Позвольте быть щедрым и предположим, что вы генерировали один токен в секунду. Временной интервал до 50% вероятности даже одного столкновения составляет 600 миллиардов лет. Планета будет поглощена солнцем задолго до этого.
Ответ 3
У меня много моделей, к которым я применяю уникальные маркеры. По этой причине я создал проблему Tokened
в app/models/concerns/tokened.rb
module Tokened
extend ActiveSupport::Concern
included do
after_initialize do
self.token = generate_token if self.token.blank?
end
end
private
def generate_token
loop do
key = SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
break key unless self.class.find_by(token: key)
end
end
end
В любой модели я хочу иметь уникальные токены, я просто делаю
include Tokened
Но да, ваш код тоже выглядит отлично.
Ответ 4
Возможно, вы можете сделать что-то, используя фактическое время. Тогда вам не нужно будет проверять, был ли токен уже использован пользователем.
new_token = Digest::MD5.hexdigest(Time.now.to_i.to_s + rand(999999999).to_s)
user.user_info_token = new_token
Ответ 5
Rails 5 поставляется с этой функцией, вам нужно только добавить к вашей модели следующую строку:
class User
has_secure_token
end
Поскольку Rails 5 еще не выпущен, вы можете использовать has_secure_token gem. Также вы можете увидеть мое сообщение в блоге, чтобы узнать больше об этом https://coderwall.com/p/kb97gg/secure-tokens-from-rails-5-to-rails-4-x-and-3-x
Ответ 6
Вы можете попробовать несколько нижеприведенных трюков, чтобы получить уникальный токен, настолько простой, что я использовал в своем проекте -
CREDIT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def create_credit_key(count = 25)
credit_key = ""
key = CREDIT_CHARS.length
for i in 1..count
rand = Random.rand((0.0)..(1.0))
credit_key += CREDIT_CHARS[(key*rand).to_i].to_s
end
return credit_key
end
Использование дайджеста снова легче, здесь я попытался создать без использования какого-либо алгоритма.