Ruby-on-rails Rails 中 Thread.current[] 使用的安全性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7896298/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 02:14:58  来源:igfitidea点击:

Safety of Thread.current[] usage in rails

ruby-on-railsthread-safety

提问by Giuseppe

I keep getting conflicting opinions on the practice of storing information in the Thread.currenthash (e.g., the current_user, the current subdomain, etc.). The technique has been proposed as a way to simplify later processing within the model layer (query scoping, auditing, etc.).

关于在Thread.current哈希中存储信息的做法(例如,current_user、当前子域等),我不断收到相互矛盾的意见。该技术已被提议作为一种简化模型层(查询范围、审计等)内后续处理的方法。

Many consider the practice unacceptable because it breaks the MVC pattern. Others express concerns about reliability/safety of the approach, and my 2-part question focuses on the latter aspect.

许多人认为这种做法是不可接受的,因为它打破了 MVC 模式。其他人表达了对该方法的可靠性/安全性的担忧,而我的 2 部分问题侧重于后一个方面。

  1. Is the Thread.currenthash guaranteed to be available and private to one and only one response, throughout its entire cycle?

  2. I understand that a thread, at the end of a response, may well be handed over to other incoming requests, thereby leaking any information stored in Thread.current. Would clearing such information before the end of the response (e.g. by executing Thread.current[:user] = nilfrom a controller's after_filter) suffice in preventing such security breach?

  1. Thread.current散列是否保证在整个周期中对一个且只有一个响应可用和私有?

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

Thanks! Giuseppe

谢谢!朱塞佩

采纳答案by Maurício Linhares

There is not an specific reason to stay away from thread-local variables, the main issues are:

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

  • it's harder to test them, as you will have to remember to set the thread-local variables when you're testing out code that uses it
  • classes that use thread locals will need knowledge that these objects are not availableto them but inside a thread-local variable and this kind of indirection usually breaks the law of demeter
  • not cleaning up thread-locals might be an issue if your framework reuses threads (the thread-local variable would be already initiated and code that relies on ||=calls to initialize variables might fail
  • 测试它们更难,因为您必须记住在测试使用它的代码时设置线程局部变量
  • 使用线程局部变量的类需要知道这些对象对它们不可,而是在线程局部变量中,而这种间接性通常违反了demeter 定律
  • 如果您的框架重用线程,则不清理线程局部变量可能是一个问题(线程局部变量已经启动,依赖||=调用初始化变量的代码可能会失败

So, while it's not completely out of question to use, the bestapproach is not to use them, but from time to time you hit a wall where a thread local is going to be the simplest possible solution without changing quite a lot of code and you will have to compromise, have a less than perfect object oriented model with the thread local or changing quite a lot of code to do the same.

因此,虽然使用并非完全没有问题,但最好的方法是不要使用它们,但有时您会碰壁,在这种情况下,本地线程将成为最简单的解决方案,而无需更改大量代码和你将不得不妥协,有一个不太完美的面向对象模型,线程本地或更改相当多的代码来做同样的事情。

So, it's mostly a matter of thinking which is going to be the best solution for your case and if you're really going down the thread-local path, I'd surely advise you to do it with blocks that remember to clean up after they are done, like the following:

所以,这主要是一个思考的问题,哪个将是您的情况的最佳解决方案,如果您真的沿着线程本地路径走,我肯定会建议您使用记得清理之后的块来做它们完成了,如下所示:

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

This ensures the thread local variable is cleaned up before being used if this thread is recycled.

如果此线程被回收,这将确保线程局部变量在使用前被清除。

回答by Dejan Simic

This little gem ensures your thread/request local variables not stick between requests: https://github.com/steveklabnik/request_store

这个小宝石确保您的线程/请求局部变量不会在请求之间粘连:https: //github.com/steveklabnik/request_store

回答by Jonny

The accepted answer covers the question but as Rails 5 now provides a "Abstract super class" ActiveSupport::CurrentAttributeswhich uses Thread.current.

接受的答案涵盖了这个问题,但随着 Rails 5 现在提供了一个使用 Thread.current的“抽象超类” ActiveSupport::CurrentAttributes

I thought I would provide a link to that as a possible(unpopular) solution.

我想我会提供一个链接作为可能的(不受欢迎的)解决方案。

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/current_attributes.rb

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/current_attributes.rb

回答by Tom Harrison

The accepted answer is technically accurate, but as pointed out in the answer gently, and in http://m.onkey.org/thread-safety-for-your-railsnot so gently:

接受的答案在技术上是准确的,但正如答案中所指出的那样,在http://m.onkey.org/thread-safety-for-your-rails 中不是那么温和:

Don't use thread local storage, Thread.currentif you don't absolutely have to

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

The gem for request_storeis another solution (better) but just read the readme there for more reasons to stay away from thread local storage.

gem forrequest_store是另一种解决方案(更好),但只需阅读那里的自述文件,就有更多理由远离线程本地存储。

There is almost always a better way.

几乎总是有更好的方法。