validform 怎么防止表单重复提交

 我来答
xiangjuan314
2016-05-24 · TA获得超过3.3万个赞
知道大有可为答主
回答量:2.9万
采纳率:0%
帮助的人:2802万
展开全部
利用同步令牌来解决重读提交的基本原理
1 用户访问包含表单的页面 服务器在这次会话中 创建一个session对象 并产生一个令牌值 将这个令牌值作为隐藏输入域值 随表单一起发送到客户端 同时将令牌值保存到session中
2 用户提交页面 服务器端首先判断请求参数中的令牌值和Session中保存的令牌值是否相等 如果相等 则清楚session的令牌值 然后执行数据处理操作 如果不相等 则提示用户已经提交过表单 同时产生一个新的令牌值保存到session中 当用户重读提交数据页面的时候 将新产生的令牌值最为隐藏输入域的值

TokenProcessor类主要提供下列方法

public java.lang.String generateToken(HttpServletRequest request)
根据当前用户会话ID和当前的系统时间生成一个唯一的令牌值

public void savaToken(HttpServletRequest request)
调用generateToken()方法产生一个令牌值 并把它保存到Session中 如果Session不存在 则创建一个新的Session

public void resetToken(HttpServletRequest request)
清楚保存在用户Session中的令牌值

public boolean isTokenValid(HttpServletRequest request)
public boolean isTokenValid(HttpServletRequest request, boolean reset)
以上两种方法获取请求参数中的令牌值 并与保存在用户Session中的令牌值进行比较 判断是否相等
参数reset表示检测后是否要清楚保存在用户Session中的令牌值 前一个方法调用后一个方法 并给reset传递参数false 即在检测后不清楚Session的令牌值

例子:
1 令牌处理类 Tokenprocessor.java
package org.sunxin.ch19.util;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

/**

* TokenProcessor类是一个单例类。

*/

public class TokenProcessor

{

static final String TOKEN_KEY="org.sunxin.token";

private static TokenProcessor instance = new TokenProcessor();

/**

* getInstance()方法得到单例类的实例。

*/

public static TokenProcessor getInstance()

{

return instance;

}

/**

* 最近一次生成令牌值的时间戳。

*/

private long previous;

/**

* 判断请求参数中的令牌值是否有效。

*/

public synchronized boolean isTokenValid(HttpServletRequest request)

{

//得到请求的当前Session对象。

HttpSession session = request.getSession(false);

if (session == null)

{

return false;

}

//从Session中取出保存的令牌值。

String saved = (String) session.getAttribute(TOKEN_KEY);

if (saved == null) {

return false;

}

//清除Session中的令牌值。

resetToken(request);

//得到请求参数中的令牌值。

String token = request.getParameter(TOKEN_KEY);

if (token == null) {

return false;

}

return saved.equals(token);

}

/**

* 清除Session中的令牌值。

*/

public synchronized void resetToken(HttpServletRequest request)

{

HttpSession session = request.getSession(false);

if (session == null) {

return;

}

session.removeAttribute(TOKEN_KEY);

}

/**

* 产生一个新的令牌值,保存到Session中,

* 如果当前Session不存在,则创建一个新的Session。

*/

public synchronized void saveToken(HttpServletRequest request)

{

HttpSession session = request.getSession();

String token = generateToken(request);

if (token != null) {

session.setAttribute(TOKEN_KEY, token);

}

}

/**

* 根据用户会话ID和当前的系统时间生成一个唯一的令牌。

*/

public synchronized String generateToken(HttpServletRequest request)

{

HttpSession session = request.getSession();

try

{

byte id[] = session.getId().getBytes();

long current = System.currentTimeMillis();

if (current == previous)

{

current++;

}

previous = current;

byte now[] = new Long(current).toString().getBytes();

MessageDigest md = MessageDigest.getInstance("MD5");

md.update(id);

md.update(now);

return toHex(md.digest());

}

catch (NoSuchAlgorithmException e)

{

return null;

}

}

/**

* 将一个字节数组转换为一个十六进制数字的字符串。

*/

private String toHex(byte buffer[])

{

StringBuffer sb = new StringBuffer(buffer.length * 2);

for (int i = 0; i < buffer.length; i++)

{

sb.append(Character.forDigit((buffer[i] & 0xf0) >> 4, 16));

sb.append(Character.forDigit(buffer[i] & 0x0f, 16));

}

return sb.toString();

}

/**

* 从Session中得到令牌值,如果Session中没有保存令牌值,则生成一个新的令牌值。

*/

public synchronized String getToken(HttpServletRequest request)

{

HttpSession session = request.getSession(false);

if(null==session)

return null;

String token=(String)session.getAttribute(TOKEN_KEY);

if(null==token)

{

token = generateToken(request);

if (token != null)

{

session.setAttribute(TOKEN_KEY, token);

return token;

}

else

return null;

}

else

return token;

}

}

2 index.jsp
增加一个隐藏域 并以服务器端产生的令牌值作为他的值

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@ include file="header.jsp" %> //封装了request.getContextPath

<%@ page import="org.sunxin.ch19.util.TokenProcessor" %>

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<base href="<%=basePath%>">

<title>My JSP 'login.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">

<meta http-equiv="cache-control" content="no-cache">

<meta http-equiv="expires" content="0">

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">

<meta http-equiv="description" content="This is my page">

</head>

<body>

<%

//获取令牌类实例

TokenProcessor processor = TokenProcessor.getInstance();

//获取令牌值

String token = processor.getToken(request);

%>

<form action="${ctx}/servlet/handle" name="theForm" method="post">

<table>

<tr>

<td>用户名:</td>

<td><input type="text" name="username"/></td>

</tr>

<tr>

<td>密码:</td>

<td>

<input type="password" name="password"/>

<%--设置隐藏域,其值为令牌值--%>

<input type="hidden" name="org.sunxin.token" value="<%=token%>"/>

</td>

</tr>

<tr>

<td>

<input type="reset" value="重设">

</td>

<td>

<input type="submit" value="提交" name="btnSubmit" >

</td>

</tr>

</table>

</form>

</body>

</html>

3 HandlerServlet.java
package org.sunxin.ch19.servlet;

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.sunxin.ch19.util.TokenProcessor;

public class HandlerServlet extends HttpServlet

{

int count=0;

public void doPost(HttpServletRequest req, HttpServletResponse resp)

throws ServletException,IOException

{

resp.setContentType("text/html;charset=GBK");

PrintWriter out=resp.getWriter();

TokenProcessor processor=TokenProcessor.getInstance();

if(processor.isTokenValid(req))

{

try

{

Thread.sleep(5000);

}

catch(InterruptedException e)

{

System.out.println(e);

}

System.out.println("submit : "+count);

if(count%2==1)

count=0;

else

count++;

out.println("success");

}

else

{

processor.saveToken(req);

out.println("你已经提交了表单,同一表单不能提交两次。");

}

out.close();

}

}
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式