• Java Web之表单重复提交问题


    上篇文章讲了验证码的制作,提及到了一个问题,就是表单重复提交的问题,可能在上次那个验证码中感觉不是那么的重要

    现在我新写一个例子,转钱。通过这个例子你就知道表单重复提交有多恐怖了。

    先来简单的介绍一下表单重复提交的3种现象

     我们来一个个的实验,首先我们写两个东东,一个jsp,一个Servlet,jsp写了就不动了,Servlet会变化,我会在不同的现象下贴出代码

    JSP:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>转账</title>
    </head>
    <body>
    <form action="/transform" method="post" >
        转账金额:<input type="text" id="money" name="money" required><br>
        <input type="submit" value="注册">
    </form>
    
    </body>
    </html>

    Servlet我们会在不同的现象修改,下面直接看

    1.服务器慢或者网络延迟,我们使用线程模拟一下

     先看Servlet

    package com.vae.RandomCode;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/transform")
    public class TransformServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            resp.setContentType("text/html;charset=utf-8");
            PrintWriter out=resp.getWriter();
            String money=req.getParameter("money");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("转出:" + money);
            out.println("转账成功");
        }
    }

    运行,如图:

    我点击注册,其实是转账。。。。

    然后应该出现一大串的转账100,但是不知道怎么回事,我用线程模拟的情况并没有出现转账多次,所以这里暂时疑惑

     2.已经提交成功,不小心刷新了页面

    (Servlet代码删了线程就行了)

    我们点击一次

    然后我不小心的刷新了几次页面

    卧槽???啥情况?我就是不小心刷新了几次,就要我多转出去钱???明显不合理

     3.已经提交成功了,我回退,再次点击提交,我就是这么的事多.....

    我点

     我回退

    我再点

    。。。。。这种情况也是不行的。。。。虽然是用户自己作死。。。。。但是还是要规避一下。。。。

    通过上面3个例子,我们知道了表单重复提交的恐怖,特别是涉及到钱的方面。所以我们要想个办法来规避这种情况,怎么办呢?

    想想,我们是不是通过两次请求就可以实现转账了,一次是JSP的按钮点击,一次是Servlet的获取数据,那么我们只需要保证我们每次的操作,都有这两次请求就可以了

    解决方案:令牌机制

    我听到这个令牌机制就想起了狄仁杰...其实令牌机制就是一个随机数UUID,和我们的验证码里面的那个是一样的。其原理是

    我在JSP页面点击按钮发出第一次请求的时候,就把我的令牌(随机数)保存到Session里面,并且把令牌传给Servlet,然后在Servlet里面判断两个是否相等

    如果相等,就ok,转账,然后立马销毁session里面的令牌,这样无论你再怎么刷新,session里面已经没有令牌了,肯定不能转账了

    话说到此,我们来看看最终的代码

    JSP:

    <%@ page import="java.util.UUID" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>转账</title>
    </head>
    <body>
    
    <%
        //生成一个令牌
        String token=UUID.randomUUID().toString();
        //存放在session中
        session.setAttribute("TOKEN_IN_SESSION",token);
    %>
    
    <form action="/transform" method="post" >
        <input type="hidden" name="token" value="<%=token%>">  <!--这个hidden是隐藏起来不可见-->
        转账金额:<input type="text" id="money" name="money" required><br>
        <input type="submit" value="注册">
    </form>
    
    </body>
    </html>

    Servlet:

    package com.vae.RandomCode;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/transform")
    public class TransformServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            //表单中的令牌
            String tokenInRequest=req.getParameter("token");
            //session中的令牌
            String tokenInSession=req.getSession().getAttribute("TOKEN_IN_SESSION").toString();
            //比较
            if (tokenInRequest.equals(tokenInSession)) {
                //立马销毁session中的令牌再说
                req.getSession().removeAttribute("TOKEN_IN_SESSION");
    
                //然后干其他的事
                resp.setContentType("text/html;charset=utf-8");
                PrintWriter out=resp.getWriter();
                String money=req.getParameter("money");
                System.out.println("转出:" + money);
                out.println("转账成功");
            }else {
                System.out.println("不让你转,重新去页面转吧");
            }
    
    
        }
    }

    运行一下,结果已经ok了,我们使用了令牌机制(就是一个随机数)实现了避免表单的重复提交。

  • 相关阅读:
    tcpprep 对IPV6的支持
    the server quit without updating pid file (/var/lib/mysql/localhost.localdomain.pid)
    servlet service() for servlet jsp throws null pointer exception
    tomcat开机启动
    mysql 允许远程访问
    spring的helloworld
    java中的那些坑
    关于struts2中的相对路径与绝对路径
    Powercenter Source Filter
    oracle删除当前用户的表
  • 原文地址:https://www.cnblogs.com/yunquan/p/10311286.html
Copyright © 2020-2023  润新知