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 в своих представлениях. Престижность Бернард Потоцкий за эту идею.