TL;DR:在使用Devise的多租户应用程序中,用户应该能够在不同租户/帐户上注册同一封电子邮件。如何让Devise使用User.find_for_database_authentication
中的account_id?
我有一个多租户应用程序。每个子域都属于一个帐户,有许多用户和管理员(不同的表,它们之间没有继承(。
class User < ApplicationRecord
belongs_to :account
devise :database_authenticatable, :confirmable, :recoverable, :rememberable
validates :email, uniqueness: true
end
class AdminUser < ApplicationRecord
belongs_to :account
devise :database_authenticatable, :confirmable, :recoverable, :rememberable
end
# config/routes.rb
scope module: 'auth' do
devise_for :users, path: ''
devise_for :admins, path: 'admin', class_name: 'AdminUser', ...
end
# /sign_in for users, /admin/sign_in for admins
# (you can log as both at the same time)
Thinks运行良好,但我需要允许用户在不同租户/帐户上使用相同的电子邮件登录。首先,我修复了验证:
class User < ApplicationRecord
validates :email, uniqueness: { scope: :account_id }
end
问题是,当用户注册/登录时,Devise会搜索第一个有电子邮件的用户,而不考虑account_id
,所以如果我在子域1和子域2上共享相同的电子邮件地址,当我登录子域2时,我会从子域1获得用户信息(这是错误的(
Devise文档建议配置request_keys
并重新定义User.find_for_database_authentication
# config/initializers/devise.rb
config.request_keys = [:subdomain]
# app/models/user.rb
def self.find_for_database_authentication warden_conditions
joins(:account).where(
email: warden_conditions[:email],
accounts: {subdomain: warden_conditions[:subdomain]}
).first
end
这类工作,但我发现了两个问题/缺点:
当我与用户注销时,它对管理员也会这样做(对开发来说有点烦人(
我想在
User.find_for_database_authentication
上使用account_id
,并避免JOIN。我不知道该怎么做,因为subdomain
是由Devise/Warden自动处理的。warden_conditions
密钥应该是email
和account_id
。
根据这个Wiki
如果您使用子域以外的列名来确定登录子域的范围,则可能必须使用authentication_keys。例如,如果您有子域表,并且在Devise模型上使用subdomain_id来确定User的范围,则必须添加authentication_keys:[:email,:subdomain_id]
所以我认为你应该
devise :database_authenticatable, :confirmable, :recoverable, :rememberable, authentication_keys[:account_id]
此外,我认为您应该重新定义find_first_by_auth_conditions,而不是find_for_database_authentication,以支持密码恢复