Rails中Thread.current []使用的安全性


81

我对在Thread.current哈希表中存储信息的做法(例如current_user,当前子域等)的看法一直存在分歧。已经提出了将该技术作为简化模型层中的后续处理(查询范围,审计等)的一种方法。

许多人认为这种做法不可接受,因为它破坏了MVC模式。其他人则对这种方法的可靠性/安全性表示担忧,而我的两部分问题主要针对后者。

  1. Thread.current在整个周期中,是否保证哈希可以对一个且只有一个响应可用并且是私有的?

  2. 我了解到,在响应结束时,线程很可能会移交给其他传入请求,从而泄漏存储在中的任何信息Thread.current。在响应结束之前清除此类信息(例如通过Thread.current[:user] = nil从控制器执行after_filter)是否足以防止此类安全漏洞?

谢谢!朱塞佩


9
在此处查看“使用Thread.current弄脏”部分。m.onkey.org/thread-safety-for-your-rails。那是一位Jruby作者写的。#1 ROR代码本身将Thread.current用于I18N和time_zone。这说明它的保证吗?#2。如果#1为true,则足够。
so_mv

谢谢,我添加了参考。
朱塞佩

3
在您链接到的文章中,这些示例仅涉及控制器层,所提出的解决方案显然是适当的。但是,我怀疑大多数人会感兴趣的是一种干净的方法,该方法可以使模型访问通常排除给他们的1-2条信息,而不必在每次调用模型时都添加额外的参数。在这方面,所有那些令人恐惧的“远离Thread.current”警告标志都没有明确的原因使我至今不确定。谢谢
朱塞佩

Answers:


48

没有远离线程局部变量的特定原因,主要问题是:

  • 测试它们比较困难,因为在测试使用它的代码时,您将必须记住要设置线程局部变量
  • 使用线程局部变量的类将需要知道这些对象对它们不可,但是在线程局部变量内,并且这种间接调用通常会破坏demeter定律。
  • 如果您的框架重用线程,则不清理线程本地可能是一个问题(线程本地变量将已经启动,并且依赖于|| =调用来初始化变量的代码可能会失败

因此,虽然使用并不是完全没有问题,但是最好的方法是不使用它们,但有时您会碰壁,在这种情况下,线程本地化将是最简单的解决方案,而无需更改大量代码和您将不得不妥协,使用一个局部线程不够完善的面向对象模型或更改大量代码来执行相同的操作。

因此,主要是要考虑哪种方法将是针对您的情况的最佳解决方案,如果您真的沿着线程本地路径前进,我一定会建议您使用记住在执行完之后要清理的块来执行此操作他们完成了,如下所示:

around_filter :do_with_current_user

def do_with_current_user
    Thread.current[:current_user] = self.current_user
    begin
        yield
    ensure
        Thread.current[:current_user] = nil
    end      
end

如果此线程被回收,这可以确保在使用线程局部变量之前对其进行清理。


1
如果您不太谨慎的话,将带有around过滤器和一个sure块一起使用将使异常处理/日志混乱。在Dejan的答案中查看RequestStore,了解基于中间件的更干净的解决方案。
Irongaze.com 2014年

解决问题的方法可能是因为我先抢救了异常(而不是像上面那样确保)。异常的调用堆栈已重置为我重新引发的位置,从而弄乱了Rails错误日志和其他地方。我最终手动将捕获的异常本人转储到日志文件中以保留它,但是它很丑陋。恕我直言,使用基于中间件的方法要干净得多。
Irongaze.com 2014年

1
因此,上面的代码仅包含一个ensure块,而不尝试捕获异常。
毛里西奥利尼亚雷斯

当您看到它时很简单:)
244an



4

可接受的答案在技术上是准确的,但是正如答案中所指出的那样,而在http://m.onkey.org/thread-safety-for-your-rails中则并非如此:

Thread.current如果您绝对不必使用线程本地存储,

gemrequest_store是另一个解决方案(更好),但是出于更多原因,请阅读此处的自述文件,以远离线程本地存储。

几乎总是有更好的方法。


3
我正在将Unicorn与Rails多租户应用程序一起使用。您能举一个“更好的方式”的例子吗?您发布的链接引发应用程序错误。谢谢。
lacostenycoder
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.