18

私は、authenticity_token を含む単純な Ruby on Rails フォームを持っています。残念ながら、このページをページ キャッシュすると認証トークンが無効になることを見逃していました。しかし、私はそれを理解できてうれしいです。

このような場合、どのようにキャッシングを解決しますか?

4

5 に答える 5

24

Matchu が投稿したように、この投稿からポイント 2 を実装できます(彼が投稿したのと同じリンクですが、私のグーグル経由でも見つかりました)。これにより、JavaScript への依存関係が追加されます。これは、必要なものである場合とそうでない場合があります。

または、 Fragment Cachingを調べることもできます。これにより、ページの特定の部分をキャッシュできますが、動的な部分 (認証トークンを含むフォームなど) を生成することもできます。この手法を使用すると、ページの残りの部分をキャッシュできますが、リクエストごとに新しいフォームを生成できます。

最終的な解決策の 1 つ (ただし、最も好ましくない) は、その特定のアクションの認証トークンを無効にすることです。これを行うには、そのフォームを生成するコントローラーの先頭に次を追加します。

protect_from_forgery :except => [:your_action]

以下を先頭に追加することで、コントローラー全体の protect_from_forgery をオフにすることもできます。

skip_before_filter :verify_authenticity_token
于 2010-03-15T17:35:36.690 に答える
1

キャッシュされたマークアップでカスタム タグをレンダリングし、すべてのリクエストでレンダリングされるフォームに置き換えることができます。

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 と同様のパターンを実装したと思います。

于 2012-07-25T14:20:25.053 に答える
1

Niklas Hofer の一般的な解決策に従いましたが、彼の実装は Rails キャッシュ ヘルパーのセマンティクスと正確に一致しないことがわかりました。safe_concatつまり、キャッシュされた HTML を、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
于 2013-05-22T20:57:48.613 に答える
1

それはうまく解決された問題ではないようです。このブログ投稿のポイント 2 では、jQuery を使用してタスクを実行する方法について説明していますが、これには Javascript の依存関係が含まれています。あなたのオプションを比較検討してください。

于 2010-03-05T04:37:56.633 に答える
0

より一般的な解決策として、キャッシュされたすべての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します。 アイデアを提供してくれた Bernard Potocki に敬意を表します。

于 2012-07-26T12:31:56.343 に答える