Rails: How to implement protect_from_forgery in Rails API mode
我有一个Rails 5 API应用程序(
最初,当我尝试呈现表单时得到了
但是,当我尝试提交此表单时,我得到:
我尝试过,
我想念什么?
更新:
为清楚起见,此应用程序的99%是纯JSON RESTful API。需要向此应用程序添加一个HTML视图和表单。因此,对于一个控制器,我想启用完整的CSRF保护。该应用程序的其余部分不需要CSRF,并且可以保持不变。
更新2:
我只是将该应用程序的HTML表单和Header的页面源与我编写的另一个常规的Rails 5应用程序进行了比较。标头中的
更新3:
好吧,我不认为不匹配是问题所在。但是,在正常工作和不正常工作的应用程序之间的进一步比较中,我注意到"网络">" Cookie"中没有任何内容。我在正常运行的应用程序的cookie中看到很多类似
问题出在这里:Rails 5,在API模式下,逻辑上不包括Cookie中间件。没有它,在验证通过表单传递的令牌时,将不会在Cookie中存储要使用的会话
有些混乱,更改
我最终在这里找到了解决该问题的答案:将cookie会话存储添加回Rails API应用程序,这导致我在这里:https://github.com/rails/rails/pull/28009/files准确地提到了以下几行我需要添加到application.rb才能使Cookies恢复工作:
1 2 3 | config.session_store :cookie_store, key:"_YOUR_APP_session_#{Rails.env}" config.middleware.use ActionDispatch::Cookies # Required for all session management config.middleware.use ActionDispatch::Session::CookieStore, config.session_options |
这三行加上:
1 2 3 4 | class FooController < ApplicationController include ActionController::RequestForgeryProtection protect_from_forgery with: :exception, unless: -> { request.format.json? } ... |
当然还有通过适当的助手生成的表格:
1 2 | form_tag(FOO_CREATE_path, method: :post) ... |
在我的Rails API应用中间找到一个受CSRF保护的表单。
如果使用的是Rails 5 API模式,则由于API是"无状态的",因此在任何视图中都不要使用
1 | protect_from_forgery unless: -> { request.format.json? } |
以便在适当的时候调用
对于AJAX调用和api不需要protect_from_forgery。
如果您想将其禁用以执行某些操作,则
1 | protect_from_forgery except: ['action_name'] |
在仅使用Rails 6 API的应用程序时遇到了这个挑战。
这是我解决的方法:
首先,将其包括在您的
1 2 3 | class ApplicationController < ActionController::API include ActionController::RequestForgeryProtection end |
注意:之所以添加它,是因为
接下来,添加跨站点请求伪造保护:
1 2 3 4 5 | class ApplicationController < ActionController::API include ActionController::RequestForgeryProtection protect_from_forgery with: :null_session end |
或者如果您想根据请求格式有条件地保护from_forgery,请执行以下操作:
1 2 3 4 5 6 | class ApplicationController < ActionController::API include ActionController::RequestForgeryProtection protect_from_forgery with: :exception if proc { |c| c.request.format != 'application/json' } protect_from_forgery with: :null_session if proc { |c| c.request.format == 'application/json' } end |
最后,将以下行添加到您的
1 | config.middleware.use ActionDispatch::Flash |
因此它将如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | module MyApp class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 6.1 # Configuration for the application, engines, and railties goes here. # # These settings can be overridden in specific environments using the files # in config/environments, which are processed later. # # config.time_zone ="Central Time (US & Canada)" # config.eager_load_paths << Rails.root.join("extras") # Only loads a smaller set of middleware suitable for API only apps. # Middleware like session, flash, cookies can be added back manually. # Skip views, helpers and assets when generating a new resource. config.api_only = true config.middleware.use ActionDispatch::Flash end end |
注意:这将防止以下错误:
1 | NoMethodError (undefined method `flash=' for #<ActionDispatch::Request:0x0000558a06b619e0>): |
仅此而已。
我希望这会有所帮助
1 2 3 | class Api::ApiController < ApplicationController skip_before_action :verify_authenticity_token end |
与上述rails5一起使用