在Rails中设计w / Subdirectories

时间:2016-01-13 14:59:22

标签: ruby-on-rails ruby devise

我正在构建一个rails应用程序,我需要通过以下方式支持多个租户:

example.com/:tenant_1
example.com/:tenant_2

对于这些租户中的每一个,我希望单独登录:

example.com/:tenant_1/sign_in
example.com/:tenant_2/sign_in

对于每个租户,我希望他们的登录会话范围限定在适用的租户子目录中。这是设计支持的东西,如果是这样的话,如何实现这个?

2 个答案:

答案 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_saltDevise::Models::DatabaseAuthenticable的默认实现只使用加密密码的前30个字符。

def User < ActiveRecord::Base
  def authenticatable_salt
    super + tenant.to_s
  end
end