Ciągle otrzymuję sprzeczne opinie na temat praktyki przechowywania informacji w Thread.current
hashu (np. Current_user, bieżąca subdomena itp.). Technika ta została zaproponowana jako sposób na uproszczenie późniejszego przetwarzania w warstwie modelu (określanie zakresu zapytań, audyt itp.).
- Dlaczego moje zmienne wątku występują sporadycznie w Railsach?
- Alternatywa dla Thread.current w opakowaniu API dla Railsów
- Czy wartości Thread.current [] i atrybuty na poziomie klasy są bezpieczne do użycia w railsach?
Wielu uważa tę praktykę za niedopuszczalną, ponieważ łamie wzorzec MVC. Inni wyrażają obawy co do niezawodności / bezpieczeństwa podejścia, a moje 2-częściowe pytanie skupia się na tym drugim aspekcie.
Czy
Thread.current
gwarantowane jest, że hash będzie dostępny i prywatny dla jednej i tylko jednej odpowiedzi przez cały cykl?Rozumiem, że wątek na końcu odpowiedzi może zostać przekazany innym przychodzącym żądaniom, powodując wyciek wszelkich przechowywanych w nim informacji
Thread.current
. Czy wyczyszczenie takich informacji przed zakończeniem odpowiedzi (np. Poprzez wykonanie odpowiedziThread.current[:user] = nil
od administratoraafter_filter
) wystarczyłoby, aby zapobiec takiemu naruszeniu bezpieczeństwa?
Dzięki! Giuseppe
źródło
Odpowiedzi:
Nie ma konkretnego powodu, aby trzymać się z daleka od zmiennych lokalnych wątku, główne problemy to:
Tak więc, chociaż nie jest to całkowicie wykluczone, najlepszym podejściem jest ich nie używać, ale od czasu do czasu trafisz na ścianę, w której wątek lokalny będzie najprostszym możliwym rozwiązaniem bez zmiany dużej ilości kodu i będziesz musiał pójść na kompromis, mieć mniej niż doskonały model obiektowy z lokalnym wątkiem lub zmienić całkiem sporo kodu, aby zrobić to samo.
Jest to więc głównie kwestia przemyślenia, które będzie najlepszym rozwiązaniem dla twojego przypadku, a jeśli naprawdę podążasz ścieżką lokalną wątku, z pewnością radzę ci to zrobić z blokami, które pamiętają o posprzątaniu po są wykonywane, jak poniżej:
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
Dzięki temu zmienna lokalna wątku zostanie wyczyszczona przed użyciem, jeśli ten wątek zostanie odtworzony.
źródło
ensure
blok i nie próbuje przechwycić wyjątku.Ten mały klejnot zapewnia, że zmienne lokalne wątku / żądania nie zatrzymują się między żądaniami: https://github.com/steveklabnik/request_store
źródło
Zaakceptowana odpowiedź obejmuje pytanie, ale jako że Rails 5 zapewnia teraz "abstrakcyjną superklasę " ActiveSupport :: CurrentAttributes która używa Thread.current.
Pomyślałem, że podam link do tego jako możliwy ( niepopularny ) rozwiązanie.
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/current_attributes.rb
źródło
Przyjęta odpowiedź jest technicznie dokładna, ale jak wskazano w odpowiedzi delikatnie, a w http://m.onkey.org/thread-safety-for-your-rails nie tak delikatnie:
Nie używaj lokalnego magazynu wątków,
Thread.current
jeśli absolutnie nie musiszKlejnotem
request_store
jest inne rozwiązanie (lepsze), ale po prostu przeczytaj plik Readme, aby uzyskać więcej powodów, aby trzymać się z daleka od lokalnego magazynu wątków.Prawie zawsze jest lepszy sposób.
źródło