Безопасность использования Thread.current [] в рельсах - PullRequest
75 голосов
/ 26 октября 2011

Я постоянно получаю противоречивые мнения о практике хранения информации в хеше Thread.current (например, current_user, текущий поддомен и т. Д.).Этот метод был предложен как способ упростить последующую обработку на уровне модели (определение объема запросов, аудит и т. Д.).

Многие считают эту практику неприемлемой, поскольку она нарушает шаблон MVC.Другие выражают озабоченность по поводу надежности / безопасности подхода, и мой вопрос, состоящий из двух частей, фокусируется на последнем аспекте.

  1. Гарантируется ли доступность хеша Thread.current и является ли он приватным для одного итолько один ответ за весь его цикл?

  2. Я понимаю, что поток в конце ответа вполне может быть передан другим входящим запросам, что приводит к утечке любой информации, хранящейся вThread.current.Будет ли достаточно очистки такой информации до конца ответа (например, путем выполнения Thread.current[:user] = nil из after_filter контроллера) для предотвращения такого нарушения безопасности?

Спасибо!Джузеппе

Ответы [ 4 ]

44 голосов
/ 28 ноября 2011

Нет особой причины держаться подальше от локальных переменных потока, основные проблемы:

  • их сложнее протестировать, так как вам придется помнить об установке локальных переменных потока при тестировании кода, который его использует
  • классы, которые используют локальные потоки, должны знать, что эти объекты не доступны для них, но находятся внутри локальной переменной потока, и этот тип косвенного обращения обычно нарушает закон деметры
  • не очистка локальных потоков может быть проблемой, если ваша среда повторно использует потоки (локальная переменная потока уже будет инициирована, и код, использующий || = вызовы для инициализации переменных, может завершиться ошибкой

Таким образом, хотя использовать его не является совершенно бесспорным, лучший подход заключается не в их использовании, а в том, что время от времени вы наталкиваетесь на стену, где локальный поток будет самым простым из возможных. решение без изменения большого количества кода, и вам придется идти на компромисс, иметь неидеальную объектно-ориентированную модель с локальным потоком или изменять довольно много кода, чтобы сделать то же самое.

Итак, это в основном вопрос мышления, которое будет лучшим решением для вашего случая, и если вы действительно идете по пути локального потока, я бы, конечно, посоветовал вам сделать это с блоками, которые помнят очистить после того, как они сделаны, как показано ниже:

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

Это гарантирует, что локальная переменная потока очищается перед использованием, если этот поток перерабатывается.

21 голосов
/ 18 декабря 2012

Этот маленький драгоценный камень гарантирует, что локальные переменные вашего потока / запроса не будут зависать между запросами: https://github.com/steveklabnik/request_store

8 голосов
/ 09 марта 2018

Принятый ответ охватывает вопрос, но так как Rails 5 теперь предоставляет «Абстрактный супер класс» ActiveSupport :: CurrentAttributes , который использует Thread.current.

Я думал, что предоставлю ссылку на это как возможное ( непопулярное ) решение.

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

3 голосов
/ 04 мая 2015

Принятый ответ технически точен, но, как указано в ответе, мягко, а в http://m.onkey.org/thread-safety-for-your-rails не так осторожно:

Не использовать локальное хранилище потоков, Thread.current если вам абсолютно не нужно

гем для request_store - это другое решение (лучше), но просто прочитайте readme там, чтобы узнать больше причин, чтобы держаться подальше от темылокальное хранилище.

Почти всегда есть лучший способ.

...