java 防止表单重复提交(serlvet)

java 防止表单重复提交


---------FormServlet.java--------------

protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub

    /*
     * 功能: 防止表单重复提交
     *
     * 生成一个表单的同时生成一个相应的令牌
     *
     * 将令牌隐藏于表单之中, 并且令牌也存入session之中
     *
     * 将用户提交表单到RegisterServlet时
     *
     * 在RegisterServlet中将表单上传的令牌与session之中的令牌对比
     *
     * 如果一致说明是正常提交, 在处理用户的提交后,要清空令牌, 避免用户重复提交
     *
     * 如果不一致说明是重复提交
     */

    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();

    // 产生一个唯一的随即码 称为令牌
    String token = TokenProcessor.getInstance().makeToken();

    // 发送表单
    out.print("<form action='/myweb/RegisterServlet' method='post'>");
    out.print("用户名 : <input type='text' name='username' /><br>");
    out.print("密码 : <input type='password' name='password' /><br>");
    out.print("<input type='hidden' name='token' value='" + token
            + "' /><br>");
    out.print("<input type='submit' value='注册' />");
    out.print("</form>");

    // 将令牌存入 session 域
    request.getSession().setAttribute("token", token);
}

--------RegisterServlet.java----------

protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    // TODO Auto-generated method stub
    response.setContentType("text/html;charset=utf-8");
    request.setCharacterEncoding("utf-8");

    PrintWriter out = response.getWriter();
    // 防止表单重复提交
    // 获得表单提交的 token 获得 session域中保存的token
    String formToken = request.getParameter("token");
    String sessionToken = (String) request.getSession().getAttribute(
            "token");
    if (formToken == null || sessionToken == null
            || !formToken.equals(sessionToken)) {
        System.out.println("表单重复提交");
        // 不是正常提交
        return;
    }
    // 获得提交的参数
    String username = request.getParameter("username");
    String password = request.getParameter("password");

    System.out.println("username=" + username);
    System.out.println("password=" + password);

    // 将 session 中的令牌置为 null
    request.getSession().removeAttribute("token");

    // 将数据插入数据库
    System.out.println("数据插入数据库.....");

    // 线程休眠
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    out.write("注册成功");
}

-----------------生成令牌----------------
// 生成令牌
public String makeToken() {
    // 生成 一个随机数
    try {
        String token = new Random().nextInt(19999999)
                + System.currentTimeMillis() + "";
        // 数据指纹算法 消息摘要 md5
        MessageDigest md = MessageDigest.getInstance("md5");

        byte[] md5 = md.digest(token.getBytes());// 获得消息摘要
        // 转为明文
        // base64 算法
        BASE64Encoder encoder = new BASE64Encoder();
        String code = encoder.encode(md5);

        return code;
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        throw new RuntimeException(e);
    }
}

----------------------------------------------------------------------------------------------------------------------------------------------

Struts中的isTokenValid(request,boolean)是用来防止表单重复提交的!   
Struts在你每次访问Action的时候,根据用户会话ID和当前系统时间对于每个会话生成一个唯一令牌,保存在你的Session里面,如果你在Action里的函数里面,使用了saveToken(request),那么这个令牌也会保存在这个Action所Forward到的jsp所生成的静态页面里。  
如果你在你Action的方法里使用了isTokenValid,那么Struts将会从你的request里面去获取这个令牌值,然后和Session里的令牌值做比较,如果两者相等,就不是重复提交,如果不相等,就是重复提交了。

一,首先介绍一下struts提供的有关令牌环的相关方法

请求有效性处理,使用令牌可以有效的防止重复提交。
protected String generateToken(HttpServletRequest request) 创建一个令牌.
protected boolean isTokenValid(HttpServletRequest request) 检查令牌是否有效
protected boolean isTokenValid(HttpServletRequest request,Boolean reset) 检查令牌是否有效,并且重置令牌(如果reset 是true)
protected void resetToken(HttpServletRequest request) 重置令牌
protected void saveToken(HttpServletRequest request) 添加令牌

二,利用struts的同步令牌机制

利用同步令牌(Token)机制来解决Web应用中重复提交的问题,Struts也给出了一个参考实现。

基本原理:

服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,
看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给
客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次
提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。

if (isTokenValid(request, true)) { 
    // your code here 
    return mapping.findForward("success"); 
} else { 
    saveToken(request); 
    return mapping.findForward("submitagain"); 
}

Struts根据用户会话ID和当前系统时间来生成一个唯一(对于每个会话)令牌的,具体实现可以参考
TokenProcessor类中的generateToken()方法。

1. //验证事务控制令牌,<html:form >会自动根据session中标识生成一个隐含input代表令牌,防止两次提交
2. 在action中:

       //<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" 
       // value="6aa35341f25184fd996c4c918255c3ae"> 
       if (!isTokenValid(request)) 
           errors.add(ActionErrors.GLOBAL_ERROR, 
                      new ActionError("error.transaction.token")); 
       resetToken(request); //删除session中的令牌

3. action有这样的一个方法生成令牌

   protected String generateToken(HttpServletRequest request) { 
       HttpSession session = request.getSession(); 
       try { 
           byte id[] = session.getId().getBytes(); 
           byte now[] = 
               new Long(System.currentTimeMillis()).toString().getBytes(); 
           MessageDigest md = MessageDigest.getInstance("MD5"); 
           md.update(id); 
           md.update(now); 
           return (toHex(md.digest())); 
       } catch (IllegalStateException e) { 
           return (null); 
       } catch (NoSuchAlgorithmException e) { 
           return (null); 
       } 
   }

原文地址:https://www.cnblogs.com/meimao5211/p/3177798.html