2010-03-05 26 views
16

Tôi có dạng Ruby on Rails đơn giản bao gồm cả tính xác thực_token. Thật không may, tôi đã bỏ lỡ khi bạn trang bộ nhớ cache trang này thì Mã xác thực sẽ trở thành không hợp lệ. Tôi rất vui vì tôi đã tìm ra nó.Bộ đệm ẩn trang biểu mẫu Ruby on Rails bao gồm authenticity_token

Làm cách nào để bạn giải quyết bộ nhớ đệm trong trường hợp này?

Trả lời

22

Khi Matchu được đăng, bạn có thể triển khai điểm hai từ this post (cùng một liên kết mà anh ấy đăng, nhưng cũng được tìm thấy qua Googling của tôi). Điều này thêm một phụ thuộc vào JavaScript, có thể hoặc không thể là thứ bạn muốn.

Hoặc, bạn có thể xem xét Fragment Caching. Điều này cho phép bạn lưu trữ một số phần nhất định của một trang nhưng vẫn tạo ra các phần động (chẳng hạn như các biểu mẫu có mã thông báo xác thực). Sử dụng kỹ thuật này, bạn có thể cache phần còn lại của trang, nhưng tạo một biểu mẫu mới cho mọi yêu cầu.

Một giải pháp cuối cùng (nhưng ít ưu tiên nhất), là vô hiệu hóa mã thông báo xác thực cho hành động cụ thể đó. Bạn có thể làm điều này bằng cách thêm những điều sau đây để đầu của bộ điều khiển tạo ra rằng hình thức:

protect_from_forgery :except => [:your_action] 

Bạn cũng có thể tắt protect_from_forgery cho toàn bộ điều khiển bằng cách thêm dòng sau vào phần đầu:

skip_before_filter :verify_authenticity_token 
+0

cảm ơn phản hồi của bạn! – jacob

1

Nó không có vẻ là một vấn đề được giải quyết tốt. Point two on this blog post mô tả cách hoàn thành nhiệm vụ bằng cách sử dụng jQuery, nhưng điều đó giới thiệu một sự phụ thuộc Javascript. Cân nhắc lựa chọn của bạn, tôi cho là vậy.

1

Bạn có thể hiển thị thẻ tùy chỉnh trong đánh dấu được lưu trong bộ nhớ cache và thay thế bằng biểu mẫu được hiển thị trên mọi yêu cầu.

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 

Và theo quan điểm của bạn, bạn chỉ cần hiển thị thẻ uncachable_form trống.

<%- cache_with_bla_form resource do %> 
    # cachable stuff.. 
    <uncachable_form /> 
    # more cachable stuff 
<%- end %> 

Có, điều này có thể không được bảo vệ, không cần JS và giảm hiệu suất từ ​​bộ nhớ đệm chỉ một chút. Tôi nghĩ rằng ai đó đã thực hiện một mô hình tương tự như một Rack Middleware.

0

Là một giải pháp tổng quát hơn, bạn cũng có thể thay thế tất cả authenticity_tokens cache với những cái hiện tại:

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 

Và sử dụng = cache_with_updated_csrf do thay vì - cache do trong quan điểm của bạn. Kudos để Bernard Potocki cho ý tưởng.

1

Tôi đã làm theo giải pháp chung của Niklas Hofer, nhưng tôi thấy rằng việc triển khai của ông không khớp với ngữ nghĩa chính xác của trình trợ giúp bộ nhớ cache Rails. Cụ thể là, nó đã cố gắng trả lại HTML đã lưu trong bộ đệm, thay vì viết nó vào bộ đệm bằng cách sử dụng safe_concat, đó là những gì mà trình trợ giúp Rails thực hiện.

Các Rails sử dụng helper là như thế này:

- cache do 
    = something 

Trong khi giải pháp của ông đòi hỏi cú pháp sau:

= cache_with_updated_csrf do 
    = something 

Đối với tính nhất quán, tôi muốn rằng những hoạt động theo cách tương tự. Do đó, tôi đã sử dụng cú pháp này:

- cache_form do 
    = something 

Đây là triển khai của tôi. Nó cũng sẽ bỏ qua bộ nhớ đệm khi bộ nhớ đệm bị tắt, như trình trợ giúp 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 
Các vấn đề liên quan