Rails中的线程本地(Thread Local)有多安全?

3
在我们的多租户Rails 4.2应用程序中,我们希望为User模型定义一个线程本地的current_token,并打算在整个应用程序中使用它。以下是current_token的定义方式:
class User < ActiveRecord::Base
  def self.current_token=(token)
    Thread.current['current_token'] = token
  end

  def self.current_token
    Thread.current['current_token']
  end
end

令牌可以通过 User.current_token 获取。在用户会话的整个生命周期中,保持 User.current_token 线程安全并活跃非常重要。需要注意的是,一些 Web 服务器(例如 puma)可能会清除线程本地。我们希望从社区获得反馈,了解线程本地在实际生产中的可靠性。我们应该注意哪些问题(如果有解决方案,则提供可能的解决方案)。

2个回答

6

Thread.current 本身是安全的。 Thread.current 的风险在于必须正确使用它,因为它不会在请求结束时自动重置。以下是一些示例:

  • 假设您在一个请求中设置了 Thread.current 中的令牌,并且在下一个请求中未设置另一个令牌(可能是因为下一个请求中没有令牌),则旧令牌仍将可用。

  • 或者您计划在每个请求结束时将 Thread.current 设为空值,以确保每个请求都从空白的 Thread.current 开始。如果一个请求由于异常而失败,它是否仍然被设为空值?

直接使用 Thread.current 存在错误的风险,并且有可能暴露来自同一服务器上后续请求(用户)的先前请求(用户)的数据。

您可能需要检查 RequestStore gem,它在内部使用 Thread.current,但是包装了一些安全层。


线程当前的旧令牌的继承是一个真正的问题。如果在一个控制器中,在before_action中清除并重置thread.current,并在after_action中清除它,是否可以消除风险? - user938363
大多数情况下,但你也必须考虑中间件。看看 RequestStore gem 如何处理这个问题。 - spickermann
请问RequestStore gem是否解决了您在帖子中提到的两个问题?只是想确认一下。我读了几篇关于RequestStore gem的文章,对于这个gem的使用还不太清楚。非常感谢。 - user938363
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - spickermann

1

我从未听说过puma和线程本地变量存在问题。你可能想使用sentient_user gem,因为它的行为非常接近你所描述的内容。唯一的区别是,它不是保留令牌,而是保留整个用户。

我注意到这种模式基本上是一个全局变量,因此它就像对使用它的任何方法而言是一个未声明的参数。但是它可以很好地用于审计,这样无论您在哪里需要它,它始终可用。


感谢您的评论。 - user938363

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接