[英]Handling CSRF authenticity token for navigator.sendBeacon requests in rails
$(window).on('unload', function() {
db.flipCounter.get(gon.slug, function(obj) {
var payload = {
slug: gon.slug,
localFlipCount: obj.fc,
time: Date.now()
}
navigator.sendBeacon('/analytics', csrfProtect(payload))
})
})
function csrfProtect(payload) {
var param = $("meta[name=csrf-param]").attr("content")
var token = $("meta[name=csrf-token]").attr("content")
if (param && token) payload[param] = token
return new Blob([JSON.stringify(payload)], { type: "application/x-www-form-urlencoded; charset=utf-8" })
}
在上面的代碼中,我希望使用有效負載將 POST 發送到“/analytics”網址。 我在嘗試觸發請求時收到以下錯誤(警告) :
Promise.js:840 未處理的拒絕:TypeError:無法在字符串 '{"slug":"test-page-by-marvin-danig","localFlipCount":1,"time":1524241435403}' 上創建屬性 'authenticity_token'
嗯。
…發出請求,我得到:
Processing by BooksController#analytics as */*
Parameters: {"{\"slug\":\"test-book-by-marvin-danig\",\"localFlipCount\":1,\"time\":1524243279653,\"param\":\"8rzDx/TNL8YeU1/NWgWSk6gB/UvmbB9Ip VajDCgfDUv5Q4pjh7x0GUG1il1jDJajtJyHf84Xv5Pt14fiCnA9w"=>"=\"}"}
Can't verify CSRF token authenticity.
exception
ActionController::InvalidAuthenticityToken
更新:問題仍未解決。 這是我現在所處的位置:
我在routes.rb
上打開了以下 GET 和 POST 路由:
# Analytics (flipCounter)
get 'auth_token', to: 'analytics#auth_token'
post 'receptor', to: 'analytics#receptor', as: :receptor
這些顯然映射到analytics_controller
像這樣:
class AnalyticsController < ApplicationController
respond_to :js
def auth_token
session[:_csrf_token] = form_authenticity_token
end
def receptor
logger.debug "Check book slug first: #{params}"
begin
book = Book.friendly.find(params[:slug])
rescue ActiveRecord::RecordNotFound => e
book = nil
end
if book.exists?
book.flipcount += params[:flipcount].to_i
end
end
private
end
除了auth_token
方法,我還得到了一個auth_token.json.erb
模板,該模板按以下方式提供:
{ "authenticity_token": "<%= session[:_csrf_token] %>" }
客戶端 javascript(草稿不佳)采用以下方式:
// When state of book changes to `not_flipping`:
flipCount += 1
const o = { slug: gon.slug, fc: flipCount }
// IndexedDb initiated elsewhere.
db.transaction('rw', db.flipCounter, function(e) {
db.flipCounter.put(o)
}).then(function(e) {
const URL = '/auth_token' // First fetch the authenticity_token!
fetch(URL, {
method: 'GET'
}).then(function(res) {
return res.json()
}).then(function(token) {
return postBookData(token)
}).catch(err => console.log(err))
}).catch(function(e) {
console.log(e)
})
function postBookData(token) {
db.flipCounter.get(gon.slug, function(obj) {
// var payload = new FormData()
// payload.append('slug', gon.slug)
// payload.append('localFlipCount', obj.fc)
// payload.append('authenticity_token', token.authenticity_token)
// payload.append('type', 'application/x-www-form-urlencoded;')
// payload.append('charset=utf-8', 'ok')
// payload.append('X-CSRF-Token', token.authenticity_token)
//var payload = { 'slug': gon.slug }
let body = {
slug: gon.slug,
flipcount: obj.fc,
time: Date.now()
}
let headers = {
type: 'application/x-www-form-urlencoded; charset=utf-8',
'X-CSRF-Token': token.authenticity_token
}
let blob = new Blob([JSON.stringify(body)], headers);
let url = '/receptor'
navigator.sendBeacon(url, blob);
}).then(function() {
flipCount = 0
var o = { slug: gon.slug, fc: flipCount }
}).catch(err => console.log(err))
}
navigator.sendBeacon
觸發的請求對象不正確,因為X-CSRF-Token
未設置,我顯然在服務器端收到以下錯誤:
Started POST "/receptor" for 127.0.0.1 at 2018-04-26 09:00:33 -0400
Processing by AnalyticsController#receptor as */*
Parameters: {"{\"slug\":\"bookiza-documentation-by-marvin-danig\",\"fc\":1}"=>nil}
Can't verify CSRF token authenticity.
exception
ActionController::InvalidAuthenticityToken
Rendering public/500.html
Rendered public/500.html (1.0ms)
Completed 500 Internal Server Error in 337ms (Views: 335.8ms | ActiveRecord: 0.0ms)
有沒有人在 Rails 應用程序上使用服務工作者在完全離線的頁面上實現了navigator.sendBeacon
場景?
剛抓住這個障礙,我的解決方案的js端如下:
window.addEventListener("unload", function() {
var url = "/your_metrics_path",
data = new FormData(),
token = $('meta[name="csrf-token"]').attr('content');
// add your data
data.append("foo", "bar");
// add the auth token
data.append("authenticity_token", token);
// off she goes
navigator.sendBeacon(url, data);
});
希望這會有所幫助。
你走在正確的軌道上,我已經成功地做到了這一點,從 DOM 中獲取 CSRF 令牌並在 JavaScript 請求中使用它。 下面是一個普通 Rails 表單在 params 中發送的內容的示例:
Started PATCH "/titles/25104" for 127.0.0.1 at 2018-04-20 14:19:11 -0700
Processing by TitlesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"N97wNps0PMEBcqEsza8gNV741uPZNmltPgJHeeBNmTF0rc2KCaePBlZeCxId+su1sdAYMsgyd/78u9S/mdmprw==" }
看起來您只需要將事情放入正確的散列結構中,您應該就可以了。 我認為您需要調整一些鍵。
我剛剛了解到,在使用 Beacon 請求時,無法自定義請求方法、提供自定義請求標頭或更改請求和響應的其他處理屬性。 請在此處查看 W3C 信標編輯器草案。
請改用 fetch api。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.