---使用HttpSession防止表单重复提交

来源:互联网 发布:淘宝10元包邮怎么赚钱 编辑:程序博客网 时间:2024/06/07 00:29

可能出现表单重复提交的情况

  1. 表单数据中的数据提交到一个servlet,servlet通过转发的方式相应了一个jsp页面(地址栏中任然是servlet的url),然后刷新当前页面。
  2. 在提交表单的时候,相应过程中重复点击。
  3. 到达相应页面后,点击浏览器返回按钮再重新点击提交。

解决办法

思想案例

思想:

在相应表单页面的时候,添加一个标记,当表单数据提交过去的时候,验证标记,并且删除标记,如果出现表单数据提交,那么就相应提示信息。

例子:
index.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%><%    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 'index.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"><!--    <link rel="stylesheet" type="text/css" href="styles.css">    --></head><body>    <%        String tokenValue = new Date().getTime() + "";        session.setAttribute("token", tokenValue);    %>    This is my JSP page.    <br>    <form action="<%=request.getContextPath()%>/tokenServlet" method="post">        <input type="hidden" name="token" value="<%=tokenValue%>"> <input            type="text" name="username" /> <input type="submit" value="submit" />    </form></body></html>

TokenServlet.java

package com.aaa.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;public class TokenServlet extends HttpServlet {    private static final long serialVersionUID = 1L;    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        HttpSession session = request.getSession();        String tokenValue = request.getParameter("token");        Object token = session.getAttribute("token");        System.out.println(token);        System.out.println(tokenValue);        if (token != null && token.equals(tokenValue)) {            session.removeAttribute("token");        } else {            response.sendRedirect(request.getContextPath() + "/token/token.jsp");            return;        }        String username = request.getParameter("username");        System.out.println("username---" + username);        request.setAttribute("username", username);        request.getRequestDispatcher("/token/success.jsp").forward(request,                response);    }}

success.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%><%    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 'success.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"><!--    <link rel="stylesheet" type="text/css" href="styles.css">    --></head><body>    <h1>        Hello        <%=request.getAttribute("username")%></h1></body></html>

token.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%    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 'token.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"><!--    <link rel="stylesheet" type="text/css" href="styles.css">    --></head><body>表单重复提交</body></html>

分析:在这个案例中,在相应index.jsp页面的时候,生成一个值tokenValue放在session(token-tokenValue)中,并且在表单中添加一个隐藏域token(token-tokenvalue),然后在表单数据提交到servlet的时候,将从隐藏域中的token和session中的token验证是否一致,一致则相应success.jsp,不一致则响应token.jsp提示表单重复提交。

使用TokenProcessor类来防止表单重复提交

使用这个的目的是为了提高代码的重用,总不可能在每次需要防止表单重复提交的地方都重写一遍这些吧,应该把他抽出来,我的理解是这个也是一种思想,将从重复的工作解放出来。

原理大都和前面类似
1. 在访问包含表单的页面的时候,产生一个token,存放在session中。
2. 在表单中提交的时候,将token放在隐藏域中提交到servlet,如果一致 则清楚session的令牌值 然后执行处理操作, 如果不相等 则提示用户已经提交过表单 同时产生一个新的token保存到session中 当用户再次到提交数据页面的时候 将新产生的令牌值最为隐藏输入域的值

TokenProcessor.java:

package com.aaa.tokenprocess;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.tuxianchao.token";    private static TokenProcessor instance = new TokenProcessor();    /**     * 得到单例的实例。     */    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);        }    }    /**     * 根据session 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;    }}

修改后的index.jsp:

<%@page import="com.aaa.tokenprocess.TokenProcessor"%><%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%><%    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 'index.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"><!--    <link rel="stylesheet" type="text/css" href="styles.css">    --></head><body>    <%        //取得TokenProcess的实例对象        TokenProcessor tokenProcessor = TokenProcessor.getInstance();        //获取token        String token = tokenProcessor.getToken(request);    %>    This is my JSP page.    <br>    <form action="<%=request.getContextPath()%>/tokenServlet1" method="post">        <!--  添加隐藏域,其值为token-->        <input type="hidden" name="org.tuxianchao.token" value="<%=token%>">        <input type="text" name="username" /> <input type="submit"            value="submit" />    </form></body></html>

修改后的TokenServlet.java:

package com.aaa.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import com.aaa.tokenprocess.TokenProcessor;public class TokenServlet1 extends HttpServlet {    private static final long serialVersionUID = 1L;    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        TokenProcessor tokenProcessor = TokenProcessor.getInstance();        if (tokenProcessor.isTokenValid(request)) {            // 合法            request.getRequestDispatcher("/token/success.jsp").forward(request,                    response);        } else {            // 不合法            tokenProcessor.saveToken(request);            response.sendRedirect(request.getContextPath() + "/token/token.jsp");        }    }}
0 0
原创粉丝点击