Ruby on Rails формирует кеширование страниц, включая authenticity_token
У меня есть простая Ruby on Rails форма, которая включает в себя authenticity_token. К сожалению, я пропустил это, когда вы перепечатываете страницу на этой странице, тогда токен подлинности становится недействительным. Я рад, что понял это.
Как вы решаете кеширование в таком случае?
Ответы
Ответ 1
Как сказал Матку, вы могли бы реализовать пункт 2 из этот пост (та же самая ссылка, которую он опубликовал, но найденный также через мой Google). Это добавляет зависимость от JavaScript, который может или не может быть тем, что вы хотите.
Кроме того, вы можете посмотреть Кэширование фрагментов. Это позволяет вам кэшировать определенные части страницы, но при этом генерировать динамические части (например, формы с токенами аутентичности). Используя этот метод, вы можете кэшировать остальную часть страницы, но генерировать новую форму для каждого запроса.
Одно окончательное решение (но наименее благоприятное) - отключить токен аутентификации для этого конкретного действия. Вы можете сделать это, добавив следующее в начало контроллера, генерирующего эту форму:
protect_from_forgery :except => [:your_action]
Вы также можете отключить защиту_from_forgery для всего контроллера, добавив в начало следующее:
skip_before_filter :verify_authenticity_token
Ответ 2
Это, похоже, не проблема. Пункт 2 в этом сообщении в блоге описывает, как выполнить задачу с помощью jQuery, но это приводит к зависимости Javascript. Взвесьте свои варианты, я полагаю.
Ответ 3
Вы можете создать собственный тег в кэшированной разметке и заменить его формой, отображаемой по каждому запросу.
module CacheHelper
# Our FORM is deeply nested in the CACHED_PARTIAl, which we
# cache. It must be rendered on every request because of its
# authenticity_token by protect_from_forgery. Instead of splitting up the
# cache in multiple fragments, we replace a special tag with the custom
# form.
def cache_with_bla_form(resource, &block)
form = nil
doc = Nokogiri::HTML::DocumentFragment.parse( capture { cache("your_cache_key",&block) } )
doc.css('uncachable_form').each do |element|
form ||= render(:partial => 'uncachable_form', :resource => resource)
element.replace form
end
doc.to_html
end
end
И, на ваш взгляд, вы просто визуализируете пустой тег uncachable_form.
<%- cache_with_bla_form resource do %>
# cachable stuff..
<uncachable_form />
# more cachable stuff
<%- end %>
Да, это можно рассматривать как Hack, но он не будет ослаблять защиту подделок, не нуждается в JS, и немного уменьшит прирост производительности от кеширования. Я думаю, что кто-то реализовал аналогичную модель промежуточного ПО Rack.
Ответ 4
Я последовал за решением Niklas Hofer, но я обнаружил, что его реализация не соответствовала точной семантике помощника кеша Rails. А именно, он попытался вернуть кэшированный HTML из помощника, вместо того, чтобы записывать его в буфер с помощью safe_concat
, что и делает помощник Rails.
Использование хелпера Rails выглядит так:
- cache do
= something
В то время как его решение требовало этого синтаксиса:
= cache_with_updated_csrf do
= something
Для согласованности я бы предпочел, чтобы они работали одинаково. Поэтому я использовал этот синтаксис:
- cache_form do
= something
Вот моя реализация. Он также пропустит кеширование, когда кеширование отключено, как и помощник Rails.
module CacheHelper
# Cache a form with a fresh CSRF
def cache_form(name = {}, options = nil, &block)
if controller.perform_caching
fragment = fragment_for(name, options, &block)
fragment_with_fresh_csrf = Nokogiri::HTML::DocumentFragment.parse( fragment ).tap do |doc|
doc.css("input[name=#{request_forgery_protection_token}]").each { |e| e['value'] = form_authenticity_token }
end.to_html
safe_concat fragment_with_fresh_csrf
else
yield
end
nil
end
end
Ответ 5
В качестве более общего решения вы также можете заменить все кэшированные authenticity_tokens на текущие:
module CacheHelper
def cache_with_updated_csrf(*a, &block)
Nokogiri::HTML::DocumentFragment.parse( capture { cache(*a,&block) } ).tap do |doc|
doc.css("input[name=#{request_forgery_protection_token}]").each { |e| e['value'] = form_authenticity_token }
end.to_html.html_safe
end
end
И используйте = cache_with_updated_csrf do
вместо - cache do
в своих представлениях. Престижность Бернард Потоцкий за эту идею.