在Rails中创建帐户时创建随机,唯一的令牌

时间:2014-12-06 19:42:01

标签: ruby-on-rails ruby devise token uniqueidentifier

我有一个使用Devise(最新版本)的Rails 4应用程序,我正在尝试为每个用户创建一个随机令牌(如ID,但更长,等等)使用this回答我能够来以下代码:

# 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

此代码非常适用于任何给定模型都独有的令牌。即所有用户都将拥有唯一的令牌,所有管理员都将拥有唯一的令牌。但是管理员可能与用户具有相同的令牌 - 此行为是不需要的。

是否有一种优雅的方式,没有将令牌抽象到自己的模型中并使用“has_one”关系,以确保令牌在其所属的所有模型中不存在?

(我想我可以将unless (User.exists? ... or Admin.exists? ... )硬编码到除非子句中,尽管这看起来很笨重。)

任何想法或建议表示赞赏!谢谢!

2 个答案:

答案 0 :(得分:2)

我会创建一个方法,列出包含我关注的每个类,然后针对每个类测试令牌。像这样:

# app/models/concerns/tokenable.rb
module Tokenable
  extend ActiveSupport::Concern

  included do
    before_create :generate_token
  end

  protected

  def included_classes
     ActiveRecord::Base.descendants.select do |c|
       c.included_modules.include(Concerns::Tokenable)}.map(&:name)
     end
  end

  def generate_token
    self.token = loop do
      random_token = SecureRandom.urlsafe_base64(nil, false)
      break random_token unless included_classes.map {|c| c.constantize.exists?(token: random_token) }.include?(true)
    end
  end
end

因此include_classes将返回一个名称数组,作为包含Tokenable关注的每个类的字符串。然后在generate_token中的循环中将检查这些类中的每一个生成一个true或false的数组,然后我们只检查include?(true)是否为真。

Here is were I found how to get included classes(第一个答案)。

修改

在Rails 5中,included_classes看起来像这样(注意ApplicationRecord而不需要Concerns :: Tokenable):

  def included_classes
    ApplicationRecord.descendants.select do |c|
      c.included_modules.include?(Tokenable)
    end
  end

答案 1 :(得分:2)

Rails 5附带了一个新的功能has_secure_token非常易于使用:

# Schema: User(token:string, auth_token:string)
class User < ActiveRecord::Base
    has_secure_token :auth_token
end

user = User.new
user.save
user.auth_token # => "pX27zsMN2ViQKta1bGfLmVJE"
user.regenerate_auth_token # => true

由于Rails 5尚未发布,您可以使用Backport has_secure_token gem