在Devise 和 Warden 源代码中挖掘了很长时间后,我终于找到了解决方案。
简短的回答:
将此添加到User类中:
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从 - 混入 User 类Devise::Models::Authenticatable::ClassMethods。老实说,我不确定它实际上应该做什么,但它是一个公共方法,并且在 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的实例,与调用. (Devise 使用gem 来保持它的灵活性;无论您使用 ActiveRecord、Mongoid 还是任何其他与 OrmAdapter 兼容的 ORM,上述方法都无需修改即可工作。)OrmAdapter::ActiveRecordUserto_adapter.getUser.findorm_adapter
但是,当然,User.find只在 default_scope 内搜索,这就是为什么它找不到我的禁止用户。调用直接to_adapter.klass返回User类,然后我可以调用unscoped.find来搜索我的所有用户并使被禁止的用户对设计可见。所以工作线是:
record = to_adapter.klass.unscoped.find(key[0])
请注意,我传递的是 Arraykey[0]而不是key,因为key它是一个 Array(在这种情况下只有一个元素),并且将 Array 传递给find将返回一个 Array,这不是我们想要的。
另请注意,klass在真正的 Devise 源代码中调用将是一个坏主意,因为这意味着您失去了OrmAdapter. 但是在您自己的应用程序中,您可以肯定地知道您正在使用哪个 ORM(Devise 不知道的东西),具体来说是安全的。