我正在尝试实现基于令牌的提交跟踪机制,以避免表单已多次提交。我的想法是生成令牌,并在表单生成后将其存储在会话中。首次检测到提交动作时,令牌将被吊销,因此,如果用户多次提交,则以下操作将不被接受,因为无法再找到有效的令牌。所以我的Python代码是这样的:
用于生成表单的代码:
from flask import Flask, render_template, request, Response, make_response, session
@app.route('/form')
def form():
...
# Create token and store in session
submit_tracking_id = uuid.uuid1()
session['submit_tracking_id'] = submit_tracking_id
...
# A copy of token would be stored in the cookie
resp.set_cookie('submit_tracking_id', str(submit_tracking_id))
return resp
表单动作服务代码:
@app.route('/action', methods=['POST'])
def action():
...
# Check token by compare cookie and session.
if 'submit_tracking_id' in request.cookies:
if uuid.UUID(request.cookies.get('submit_tracking_id')) == session.pop('submit_tracking_id', None):
# Long time processing...
...
else:
...
...
我希望session.pop
会在第一次检索到时撤消'submit_tracking_id'。但是现实让我感到惊讶的是,如果我在if
命令下面的下一行(在执行session.pop
之后)设置一个断点来暂停上一个过程,并立即触发另一个提交,第二个请求仍将能够获得令牌!在对比方面,如果我在第一个请求完全完成后触发第二个请求,则第二个请求不会被捕获。为什么会话在上一个请求返回之前没有反映出更改?
用于查询服务的客户端javascript如下:
const test_form = document.forms.namedItem("test_form");
test_form.addEventListener('submit', ev => {
ev.preventDefault();
__submit(__read_form_data('test_form')).then(html => {
document.getElementById("result").innerHTML = html
})
})
__submit = (data) => {
return fetch('/action', {
method: 'POST',
body: data
}).then(response => {
if(response.status == '200') {
return response.text()
} else
return ''
})
}
有没有人可以解释会话存储的工作原理?实施该机制的正确方法是什么?