什么情况下会导致重复提交?
1.网络延迟,当用户提交后由于网络的原因在等待提交,如果这个时候用户再次点击提交按钮的话就会导致重复提交。
2.如果提交以后,这时用户如果点击刷新按钮的话,就会重复的提交。
3.用户提交以后,如果用户点击返回按钮,再点击提交也会实现重复提交。
解决方法有两种:
1.在客户端使用javaScript防表单重复提交
2.在服务器端写程序防重复提交
示例:客户端使用javaScript防表单重复提交
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>form.html</title>
- <meta http-equiv="content-type" content="text/html; charset=UTF-8">
- <script type="text/javascript">
- var iscommiteed = false;
- function formsubmit(){
- if(!iscommiteed){
- iscommiteed = true;
- return true;
- }else{
- alert("请不要重复提交!!");
- return false;
- }
- }
- </script>
- </head>
- <body>
- <form action="/day06/servlet/RegisterServlet" method="post" onsubmit="return formsubmit()">
- 用户名:<input type="text" name="username">
- <input type="submit" value="注册">
- </form>
- </body>
- </html>
这种提交只能防住第一种(网络延迟),后两种是防不住,三种为了都能防住只能在服务器端写程序防重复提交。
服务器端写程序防重复提交实现原理:
当用户提交的时候,系统首先需要生成一个随机号,在服务器端进行保存并在jsp表单中绑定这个生成的随机号,当jsp表单提交时服务器端接收用户提交的这个随机号然后与服务器端保存的随机号进行对比,如果随即号相同的话,执行提交操作,并把服务器端保存的随机号销毁。如果不同的话提示用户重复提交。
示例:生成随机数
- package cn.itcast.form;
- import java.io.IOException;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import java.util.Random;
- import java.util.UUID;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import sun.misc.BASE64Encoder;
- public class FormServlet extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- String token = TokenProcessor.getInstance().getToken();
- request.getSession().setAttribute("token", token);
- request.getRequestDispatcher("/form.jsp").forward(request, response);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
- //令牌(随机数生成器)
- class TokenProcessor{
- /**
- * 1.把构造方法私有
- * 2.自己创建一个对象
- * 3.定义一个方法,让别人获取这个对象
- *
- */
- private TokenProcessor(){}
- private static final TokenProcessor instance = new TokenProcessor();
- public static TokenProcessor getInstance(){
- return instance;
- }
- public String getToken(){
- String token = System.currentTimeMillis() + new Random().nextInt(1000000) + "";
- //md5 数据指纹 消息摘要
- try {
- MessageDigest digest = MessageDigest.getInstance("md5");
- byte md5[] = digest.digest(token.getBytes()); //任意的16个字节 3434
- //Base64编码
- //BASE64Encoder encoder = new BASE64Encoder();
- //return encoder.encode(md5);
- return toHex(md5);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- //return UUID.randomUUID().toString();
- }
- public String toHex(byte b[]){
- StringBuffer sb = new StringBuffer();
- for(int i=0;i<b.length;i++){ //1100 0011
- sb.append(Character.forDigit(b[i]&15,16)); //10 A 9 9 12 C
- sb.append(Character.forDigit(b[i]>>4&15,16));
- }
- return sb.toString();
- }
- }
服务器端验证:
- package cn.itcast.form;
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class RegisterServlet extends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //防表单重复提交
- boolean b = isValid(request);
- if(!b){
- System.out.println("表单是重复提交!!");
- return;
- }
- request.getSession().removeAttribute("token");
- String username = request.getParameter("username");
- try {
- Thread.sleep(1000*5);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println("向数据库注册用户");
- System.out.println("注册成功!!");
- }
- //校验客户机带过来随即号否有效
- private boolean isValid(HttpServletRequest request) {
- String c_token = request.getParameter("token");
- if(c_token==null || c_token.equals("")){
- return false;
- }
- String s_token = (String) request.getSession().getAttribute("token");
- if(s_token==null){
- return false;
- }
- if(!c_token.equals(s_token)){
- return false;
- }
- return true;
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
用Struts1防重复提交:
以上示例就是模拟struts1来提交的,原理是一样的,如果用struts1来实现访重复提交会更加简单,struts1内部封装了很多操作。
注意:jsp页面中只需用struts标签的form表单就可以自动获取表单号。
示例:生成随机数
- package cn.itcast.web.action;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.struts.action.Action;
- import org.apache.struts.action.ActionForm;
- import org.apache.struts.action.ActionForward;
- import org.apache.struts.action.ActionMapping;
- public class RegisterUIAction extends Action {
- @Override
- public ActionForward execute(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- //产生一个表单号,并存储到session域中
- saveToken(request);
- return mapping.findForward("registerUI");
- }
- }
服务器端验证:
- package cn.itcast.web.action;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.struts.action.Action;
- import org.apache.struts.action.ActionForm;
- import org.apache.struts.action.ActionForward;
- import org.apache.struts.action.ActionMapping;
- import cn.itcast.web.formbean.RegisterFormBean;
- //防表单重复提交
- public class RegisterAction extends Action {
- @Override
- public ActionForward execute(ActionMapping mapping, ActionForm form,
- HttpServletRequest request, HttpServletResponse response)
- throws Exception {
- //校验表单提交的是否有效,如果有效的话就要把表单号清掉(true的含义)
- boolean b = this.isTokenValid(request,true);
- if(!b){
- System.out.println("重复提交");
- return null;
- }
- RegisterFormBean formbean = (RegisterFormBean) request.getAttribute("registerFormBean");
- try{
- System.out.println("向数据库注册" + formbean.getUsername());
- request.setAttribute("message", "注册成功!!");
- return mapping.findForward("message");
- }catch (Exception e) {
- e.printStackTrace();
- request.setAttribute("message", "注册失败!!");
- return mapping.findForward("register");
- }
- }
- }