我正在构建一个rails应用程序,我需要通过以下方式支持多个租户:
example.com/:tenant_1
example.com/:tenant_2
对于这些租户中的每一个,我希望单独登录:
example.com/:tenant_1/sign_in
example.com/:tenant_2/sign_in
对于每个租户,我希望他们的登录会话范围限定在适用的租户子目录中。这是设计支持的东西,如果是这样的话,如何实现这个?
答案 0 :(得分:5)
如果您拥有模型且sessions
设置正确,则Devise将支持身份验证。
-
如果你有一个subdomain
:
#config/routes.rb
scope Tenant do
root "application#dashboard"
devise_for :users, controllers: {}
end
#lib/tenant.rb
module CompanyDispatch
def initializer(router)
@router = router
end
def self.matches?(request)
Account.exists? request.path.split("/").first
end
end
以上将使您能够访问url.com/:tenant_1/sign_in
(可能需要调整等)。
您可能需要调整Devise
方法 - 使用Warden
策略或其find
methods:
# app/models/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, request_keys: [:x]
def self.find_for_authentication(warden_conditions)
account = Account.find warden_conditions[:x]
where(email: warden_conditions[:email], account_id: account.id ).first
end
end
上面的代码需要传递给Devise的正确请求参数;然后,您需要确保session
仅限于每个租户:
#config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store, key: '_[app_name]_session' #-> need to add something here so paths will be used
-
这是你如何做到这一点。上面的代码需要调整,但我现在非常累,所以我会事后更新,除非你更愿意我删除它。
答案 1 :(得分:0)
一个非常简单的实现:
根据How To: Change the default sign_in and sign_out routes
devise_for :users, :skip => [:sessions]
as :user do
get ':tenant_slug/signin' => 'devise/sessions#new', :as => :new_user_session
post ':tenant_slug/signin' => 'devise/sessions#create', :as => :user_session
delete ':tenant_slug/signout' => 'devise/sessions#destroy', :as => :destroy_user_session
end
您可以将会话cookie的范围限定为特定路径,但由于cookie由用户控制,因此无法提供任何真正的安全性。
替代方案是ApplicationController
中的过滤器,以确保用户只能访问包含其tenant_slug
的路径。这假设用户的所有路由都在他自己的租户slug下命名。
class ApplicationController < ActionController::Base
before_action :ensure_user_belongs_to_tenant
def ensure_user_belongs_to_tenant
redirect_to tenant_root_path(current_user.tenant) unless params[:tenant_slug] == current_user.tenant.slug
end
end
要在身份验证期间包含租户,您可以将租户的名称连接到Model#authenticable_salt
。 Devise::Models::DatabaseAuthenticable
的默认实现只使用加密密码的前30个字符。
def User < ActiveRecord::Base
def authenticatable_salt
super + tenant.to_s
end
end