Rails:基于子域的查询范围的最佳实践?

我正在开发一个Rails(目前是2.3.4)应用程序,该应用程序利用子域来隔离独立的帐户站点。 要明确的是,我的意思是foo.mysite.com应该显示foo帐户的内容,bar.mysite.com应该显示栏的内容。

确保所有模型查询都限定在当前子域的最佳方法是什么?

例如,我的一个控制器看起来像:

@page = @global_organization.pages.find_by_id(params[:id]) 

(注意@global_organization是通过subdomain-fu在application_controller中设置的。)当我喜欢的是:

 @page = Page.find_by_id(params[:id]) 

Page模型找到的位置自动限定在正确的组织中。 我尝试过使用default_scope指令,如下所示:(在Page模型中)

 class Page  "organization_id = #{Thread.current[:organization]}" # yadda yadda end 

(再次,请注意,相同的application_controller将Thread.current [:organization]设置为组织的全局访问ID。)此方法的问题是默认范围在第一个请求上设置,并且永远不会在后续请求中更改不同的子域名。

到目前为止有三种明显的解

1为每个子域使用单独的vhost,并且每个子域只运行应用程序的不同实例(使用mod_rails)。 此方法不适用于此应用。

2使用上面的原始控制器方法。 不幸的是,应用程序中有相当多的模型,许多模型都是从组织中删除的一些连接,因此这种符号很快变得很麻烦。 更糟糕的是,这主动要求开发人员记住并应用限制或冒重大安全问题。

3使用before_filter重置每个请求的模型的默认范围。 不确定此处的性能或如何最好地选择要更新的模型。

思考? 我还缺少其他任何解决方案吗? 这似乎是一个普遍的问题,必须有一个最佳实践。 感谢所有投入,谢谢!

这里要小心使用默认范围,因为这会导致您产生错误的安全感,特别是在创建记录时。

我总是用你的第一个例子来说明这一点:

 @page = @go.pages.find(params[:id]) 

最大的原因是因为您还希望确保将此关联应用于新记录,因此您的新/创建操作将如下所示,确保它们适当地限定为父关联:

 # New @page = @go.pages.new # Create @page = @go.pages.create(params[:page]) 

您是否尝试过将default_scope定义为lambda ? 每次使用范围时,都会评估定义选项的lambda位。

 class Page < ActiveRecord::Base default_scope lambda do {:conditions => "organization_id = #{Thread.current[:organization]}"} end # yadda yadda end 

它基本上是你的第三个选择,通过与你之前的过滤魔法一起工作。 但它比那更具攻击性,在页面模型上使用的每个单一的查找。

如果你想为所有模型使用这种行为,你可以将default_scope添加到ActiveRecord :: Base,但是你提到一些是几个连接。 因此,如果您采用此路线,则必须覆盖这些模型中的默认范围以解决连接问题。

我们在过去的两年里一直在使用https://github.com/penguincoder/acts_as_restricted_subdomain ,但它只适用于Rails 2.3。

我们目前正在尝试升级(和gemmify)插件以使用Rails 3.我很好奇你是如何解决问题的。

您可能更好的是为每个帐户建立一个数据库并根据子域切换数据库连接 。

除了上面的链接,如果你有一个模型(在你的情况下是帐户)你想使用默认数据库,只需在模型中包含establish connection

 class Account < ActiveRecord::Base # Always use shared database establish_connection "shared_#{RAILS_ENV}".to_sym 

我发现acts_as_tenant gem [ github ]非常适合这个function。 它以最小的额外努力为您确定范围,并且还使绕过范围很难。

这是关于它的最初博客文章: http : //www.rollcallapp.com/blog/2011/10/03/adding-multi-tenancy-to-your-rails-app-acts-as-tenant