我提出了一种技术,通过返回/转发或刷新页面来防止重复表单提交.我想在这里讨论它,我已经测试了一个不在生产环境中的样本,你可以识别的缺陷是什么?
请注意,我很清楚使用表单令牌,它将保护您免受CSRF攻击,并且未在以下步骤中添加.
- 为每个表单生成表单ID,并将其用作表单中的隐藏字段:
$formid = microtime(true)*10000;
Run Code Online (Sandbox Code Playgroud)
- 表格提交:
从数据验证
计算表单字段数据的哈希值
$allvals = '';
foreach($_POST as $k=>$v){
$allvals .= $v;
}
$formHash = sha1($allvals);
Run Code Online (Sandbox Code Playgroud)通过与先前保存的哈希进行比较来验证表单哈希.会话值通过$ formid变量绑定到每个表单.
$allowAction = true;
if(isset($_SESSION['formHash'][$_POST['formid']]) && ($_SESSION['formHash'][$_POST['formid']] == $formHash)){
$allowAction = false;
}
Run Code Online (Sandbox Code Playgroud)如果保存了数据(例如,保存到数据库),请将表单哈希保存到会话中:
$_SESSION['formHash'][$_POST['formid']] = $formHash;
Run Code Online (Sandbox Code Playgroud)完整版代码:http: //thebusy.me/2011/01/06/preventing-duplicate-form-submissions/
实现您想要的更简单的方法是在提交时使用重定向.处理POST请求后,您可以重定向,甚至可能重定向到同一页面.这是一种称为"POST后重定向"或POST/Redirect/GET的常见模式.
例如:
<?php
if($_POST) {
// do something
// now redirect
header("Location: " . $_SERVER["REQUEST_URI"]);
exit;
}
?>
<html> ...
<form method="post" action=""> ... </form>
Run Code Online (Sandbox Code Playgroud)
通过将操作设置为"",它将提交给自己,此时if($ _ POST)代码块将验证为true并处理表单,然后重定向回自身.
当然,您可能希望重定向到显示"您的表单已提交"响应的不同页面,或将表单放在不同的页面上,并将此页面的HTML作为响应.
这种方法的好处是当你点击后退按钮时,它会执行GET请求,因此不会重新提交表单.
在Firefox上,它实际上会将提交内容从浏览器历史记录中删除,因此当用户浏览网页然后回滚时,他们会看到表单页面,而不是看到"谢谢"页面.