Кэширование страницы формы Ruby on Rails, включая authenticity_token - PullRequest
18 голосов
/ 05 марта 2010

У меня есть простая форма Ruby on Rails, которая включает в себя authenticity_token. К сожалению, я пропустил, что когда вы кешируете страницу, то токен аутентификации становится недействительным. Я рад, что понял это, однако.

Как вы решаете кеширование в таком случае?

Ответы [ 5 ]

23 голосов
/ 15 марта 2010

Как писал Matchu, вы можете реализовать пункт два из этого поста (ту же ссылку, которую он разместил, но также нашел в моем Google) Это добавляет зависимость от JavaScript, который может быть или не быть тем, что вы хотите.

Кроме того, вы можете посмотреть Кэширование фрагментов . Это позволяет вам кэшировать определенные части страницы, но при этом генерировать динамические части (например, формы с токенами подлинности). Используя эту технику, вы можете кэшировать оставшуюся часть страницы, но генерировать новую форму для каждого запроса.

Одно окончательное решение (но наименее благоприятное) - отключить маркер подлинности для этого конкретного действия. Вы можете сделать это, добавив следующее в начало контроллера, генерирующего эту форму:

protect_from_forgery :except => [:your_action]

Вы также можете отключить protect_from_forgery для всего контроллера, добавив в начало следующее:

skip_before_filter :verify_authenticity_token
1 голос
/ 23 мая 2013

Я следовал общему решению Никласа Хофера, но обнаружил, что его реализация не соответствует точной семантике хелпера 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
1 голос
/ 25 июля 2012

Вы можете отобразить пользовательский тег в разметке кэша и заменить его формой, отображаемой при каждом запросе.

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 %>

Да, это можно считать взломом, но это не ослабит защиту от подделки, не требует JS и лишь немного снизит выигрыш в производительности от кэширования. Я думаю, что кто-то реализовал аналогичную модель как Rack Middleware.

1 голос
/ 05 марта 2010

Это не кажется хорошо решенной проблемой. Второй пункт в этом сообщении в блоге описывает, как выполнить задачу с помощью jQuery, но при этом появляется зависимость Javascript. Взвесьте ваши варианты, я полагаю.

0 голосов
/ 26 июля 2012

В качестве более общего решения вы также можете заменить все кэшированные 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 в своих представлениях. Престижность Бернар Потоцкий за идею.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...