您的位置:首页 > 编程语言 > Java开发

javaweb之Session客户端防表单重复提交(js)和服务端Session防表单重复提交

2014-03-11 10:55 453 查看
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>提交表单</title>
<!-- 光有js是不能阻止表单重复提交的,比如别人直接拿你的源代码去掉js,修改地址后是可以提交的 ,js是防不死的,有很多漏洞,利用刷新后退等也可以提交多次-->
<!-- 客户端用js阻止表单重复提交 -->
<script type="text/javascript">
//方法一:只提交一次表单
//设置好一个是否已经提交的全局变量
var iscommitted = false;
function dosubmit() {
//如果还没有提交
if(!iscommitted) {
//将全局变量设置为true,表示已经提交
iscommitted = true;
//返回可以提交
return true;
} else {
//如果已经提交,返回不可以提交
return false;
}
}

/*
方法二:
//点击提交后设置按钮不可以再点击
function dosubmit() {
var input = document.getElementById("submit");
input.disabled = 'disabled'
return true;
}
*/
</script>
</head>
<body>
<form action="/day07/servlet/DoFormServlet" method="post" onsubmit="return dosubmit()">
用户名:<input type="text" name="username"/>
<input id="submit" type="submit" value="登录"/>
</form>
</body>
</html>
package test.form;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import sun.misc.BASE64Encoder;

/*
* 服务器端防表单重复提交思路:
*	要在服务器端防表单重复提交,表单应该有一个程序输出浏览器,
*	程序在把表单打给浏览器的时候,在每一个表单中都带上一个唯一的表单号,
*	并且服务器会把这个唯一的表单号在服务器端也存储一份,
*	浏览器提交表单的时候会带着表单号过来,服务器检测带过来的表单号在服务器端有没有,
*	如果有的话代表这个表单没有提交过,就让这个表单号提交,
*	然后马上把这个已经提交了的表单号在服务器端删除,下次再提交的时候带表单号过来,
*	服务器端没有表单号了,服务器端就不让表单提交了。
*/
//产生表单的servlet
public class FormServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 产生随机数(表单号)
TokenProcessor tp = TokenProcessor.getInstance();
String token = tp.generateToken();

// servlet不适合输出表单,要转发到jsp页面,也要把表单号带过去,并且以后防止表单重复提交的时候还要用这个表单号,所以要存在session中,不能存在request中
request.getSession().setAttribute("token", token);
request.getRequestDispatcher("/form2.jsp").forward(request, response);
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

}

}

// 产生随机表单号的类
class TokenProcessor { // Token令牌
// 为了保证令牌发生器产生的令牌是唯一的,通常会把这个令牌发生器设计成单立的,因为一个对象创建的随机数重复的概率低
private TokenProcessor() {
}

private static final TokenProcessor instance = new TokenProcessor();

public static TokenProcessor getInstance() {
return instance;
}

// 产生随机数的方法
public String generateToken() {
// 根据当前毫秒值和一个随机数产生随机数,但是随机数长度不一致
String token = System.currentTimeMillis() + new Random().nextInt() + "";
// 希望得到随机数长短一样,要拿到随机数的数据摘要(指纹),不管人有多大,指纹都是一样的,不管数据多大,数据指纹始终是128bit(128位)
// 运用指纹算法,得到固定长度的随机数
try {
// 得到数据的摘要(指纹)的算法,用md5算法算出数据摘要
MessageDigest md = MessageDigest.getInstance("md5");
// 对接收的数据进行摘要运算,得到数据摘要,数据不管多大,返回的指纹只有128bit(16个字节)
byte[] md5 = md.digest(token.getBytes());
// 不能直接用这个字节数据构建字符串返回,这样的话,没有指定码表,肯定会是乱码
// return new String(md5);
// 要用base64编码产生字符串,任何数据经过base64编码返回的都是明文字符串,键盘上能看到的字符组成的字符串。把三个字节变成四个字节。
// base64有一个自己定义的码表,它会查自己的码表
BASE64Encoder encoder = new BASE64Encoder();
// 返回base64编码后的字符串
return encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}

}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>由程序输出,带有随机表单号的表单</title>
</head>
<body>
<form action="/day07/servlet/DoFormServlet" method="post">
<!-- 表单号由隐藏域提交过去 -->
<!-- 千万注意!在EL表达式后面千万不要多空格!否则数据后面也会多空格! -->
<input type="hidden" name="token" value="${token}"/>
用户名:<input type="text" name="username"/><br />
<input id="submit" type="submit" value="提交"/>
</form>
</body>
</html>

package test.form;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//处理表单提交servlet(防表单重复提交功能和struts做法一样)
//防表单重复提交要js和服务端都阻止,这才能彻底的防止表单重复提交
public class DoFormServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
form2Test(request);
}

// 处理form2页面提交的请求(服务器端防表单重复提交)
private void form2Test(HttpServletRequest request) {
// 防止表单重复提交代码
// 检查表单号是否有效
boolean b = isTokenValid(request);
// 如果是true,意味着表单号有效,可以提交,如果是false,表单号无效,阻止表单提交
if (!b) {
// 阻止提交其实可以什么都不干(没有响应),不过为了看阻止效果,输出一句话
System.out.println("请不要重复提交");
return;
}

// 如果程序没有返回,执行到这里代表表单号有效
// 处理表单提交之前,表单号要置为无效(从服务器端把已经提交过的表单号删掉)
request.getSession().removeAttribute("token");
// 处理表提交,向数据库中注册用户
System.out.println("向数据库中注册用户");
}

// 处理form页面提交的请求(js防表单重复提交)
private void formTest(HttpServletRequest request) {
String username = request.getParameter("username");

// 模仿网络延迟效果
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("向数据库中注册用户~~~~");
}

// 检查表单号是否有效
private boolean isTokenValid(HttpServletRequest request) {
// 得到客户机带过来的表单号
String client_token = request.getParameter("token");
// 没有带表单号过来认为是重复提交
if (client_token == null) {
return false;
}
// 判断服务器中有没有客户机带过来的表单号
// 拿到服务器中的表单号
String server_token = (String) request.getSession().getAttribute(
"token");
// 判断服务器中有没有表单号,如果服务器中没有表单号,服务器端就已经把表单号提交了、删除了,没有表单号就代表提交过了
if (server_token == null) {
return false;
}
// 判断服务器端的表单号和客户机带过来的表单号是否一致,不一致认为是重复提交
if (!server_token.equals(client_token)) {
return false;
}
// 如果上面的检查都通过了,就代表可以提交
return true;
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}

/*
* 验证本程序的时候先在浏览器中提交一次(第一次提交是正常的,不是重复提交),然后刷新页面或者后退一次再提交,就会看到重复提交提示。
*/
// 千万注意!在EL表达式后面(${}后面,${里面可以有空格})千万不要多空格!否则数据后面也会多空格!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: