我有一个 Rails 4 应用程序,它使用 Devise 3.4 进行身份验证,我已经定制了禁止用户的能力(使用简单的 bool 列 users.banned
,默认为 false)。 User
型号也有default_scope
它只返回非禁止用户。
这就是问题所在 - 我仍然希望我的被禁止用户能够登录,即使他们在登录后无法做任何事情。(他们基本上只是看到一个页面说“你已被禁止”)。但似乎 default_scope
正在绊倒设计。当您登录或打电话时,例如authenticate_user!
, Devise 尝试使用基本的 ActiveRecord 方法之一来查找当前用户,例如 find
或 find_by
,但不能,因为它们位于默认范围之外。因此,Devise 得出用户不存在的结论,并且登录失败。
如何让设计忽略默认范围?
最佳答案
在 之后长 在 Devise 和 Warden 源代码中四处挖掘的时间,我终于找到了解决方案。
简短的回答:
将此添加到 User
类(class):
def self.serialize_from_session(key, salt)
record = to_adapter.klass.unscoped.find(key[0])
record if record && record.authenticatable_salt == salt
end
(请注意,我只为 ActiveRecord 测试过这个;如果您使用不同的 ORM 适配器,您可能需要更改方法的第一行......但是我不确定其他 ORM 适配器是否甚至有这个概念的“默认所以
长答案:
serialize_from_session
从 - Devise::Models::Authenticatable::ClassMethods
混入 User 类.老实说,我不确定它实际上应该做什么,但它是一个公共(public)方法,并且在 Devise API 中记录(非常稀疏),所以我认为它在没有警告的情况下从 Devise 中删除的可能性不大。这是 Devise 3.4.1 的原始源代码:
def serialize_from_session(key, salt)
record = to_adapter.get(key)
record if record && record.authenticatable_salt == salt
end
问题在于
to_adapter.get(key)
. to_adapter
返回 OrmAdapter::ActiveRecord
的实例缠绕User
类和 to_adapter.get
与调用 User.find
基本相同. (Devise 使用 orm_adapter
gem 来保持它的灵 active ;无论您使用 ActiveRecord、Mongoid 还是任何其他与 OrmAdapter 兼容的 ORM,上述方法都无需修改即可工作。)但是,当然,
User.find
仅在 default_scope 内搜索,这就是为什么它找不到我被禁止的用户的原因。调用to_adapter.klass
返回 User
直接上课,然后我可以调用unscoped.find
搜索 全部 我的用户并使被禁止的用户对设计可见。所以工作线是:record = to_adapter.klass.unscoped.find(key[0])
请注意,我正在传递
key[0]
而不是 key
, 因为 key
是一个数组(在这种情况下只有一个元素)并将一个数组传递给 find
将返回一个数组,这不是我们想要的。另请注意,调用
klass
在真正的 Devise 源代码中将是一个坏主意,因为这意味着您失去了 OrmAdapter
的优势.但是在您自己的应用程序中,您可以肯定地知道您正在使用哪个 ORM(Devise 不知道的东西),具体来说是安全的。
关于ruby-on-rails - 当 Devise 用户超出我的默认范围时,如何允许他们登录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29813744/