我有一个使用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? ... )
硬编码到除非子句中,尽管这看起来很笨重。)
任何想法或建议表示赞赏!谢谢!
答案 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