使用Devise在多租户应用程序上为不同帐户使用相同的电子邮件



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

这类工作,但我发现了两个问题/缺点:

  1. 当我与用户注销时,它对管理员也会这样做(对开发来说有点烦人(

  2. 我想在User.find_for_database_authentication上使用account_id,并避免JOIN。我不知道该怎么做,因为subdomain是由Devise/Warden自动处理的。warden_conditions密钥应该是emailaccount_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,以支持密码恢复

最新更新