方法一:利用struts2中的token拦截器
struts2中有一个token拦截器,但不是默认拦截器栈中的一个,所以此处用token拦截器来阻止表单重复提交
创建表单jsp页面:
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <%@ taglib uri="/struts-tags" prefix="s"%> <!-- 引入struts2标签库 --> 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 5 <html> 6 <head> 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 8 <title>My First Struts2 Project</title> 9 </head> 10 <body> 11 <form action="${pageContext.request.contextPath }/stopRepeat/login_login.action" method="post"> 12 <s:token/><!-- 添加防止重复提交标签 --> 13 用户名<input type="text" name="name"><br> 14 手机号<input type="text" name="mobile"><br> 15 <input type="submit" value="登录"> 16 </form> 17 </body> 18 </html>
Action方法代码:(Action类要继承ActionSupport类)
1 package com.bjyinfu.struts.actions; 2 3 4 import com.opensymphony.xwork2.ActionSupport; 5 6 public class LoginAction6 extends ActionSupport { 7 8 private String mobile; 9 private String name; 10 public String getMobile() { 11 return mobile; 12 } 13 public void setMobile(String mobile) { 14 this.mobile = mobile; 15 } 16 public String getName() { 17 return name; 18 } 19 public void setName(String name) { 20 this.name = name; 21 } 22 public String login(){ 23 return "success"; 24 } 25 }
注册Action方法:
重复提交报错视图:
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <title>My First Struts2 Project</title> 8 </head> 9 <body> 10 <h1>请求正在处理!请稍后。。。。。。</h1> 11 </body> 12 </html>
引入struts2标签库,在表单内部添加令牌标签<s:token/>,当浏览器访问此jsp页面时,服务器会知道此jsp中有表单,并且表单进行了防止提交,此时服务器会生成令牌给表单一份,服务器自己留一份与之对应的令牌,第一次访问此表单时令牌对应的,此时服务器发现令牌对应后,会立即将自己手中的令牌进行修改,而表单中的令牌内容没有变化,所以当由于网络原因服务器未进行回应的时候再次提交表单,此时表单中的令牌已经与服务器中的令牌不对应,服务器会抛出异常自动转跳到"invalid.token"视图上,而不会将表单再次提交,这就是struts2的token令牌机制;而抛出的异常转跳可以注册到struts.xml中对应的Action方法中来实现程序的健壮;
重复提交时:![](https://images2017.cnblogs.com/blog/1093243/201708/1093243-20170829170300765-224287536.png)
回退到表单中:
方法二:手动编写令牌机制,利用session
手工方法一:该方法原理是 记住上一次提交的 页面token。将本次的token和上次比对,如果一样说明重复提交。该方法不需要禁用缓存。浏览器回退再次进行提交,必须进行刷新界面才能生效
jsp代码:
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <title>Insert title here</title> 8 </head> 9 <body> 10 <% 11 long token=System.currentTimeMillis(); //产生时间戳的token放到session中 12 session.setAttribute("token",token); 13 %> 14 15 <form action="${pageContext.servletContext.contextPath }/formSubmit/submit_.action" method="post"> 16 <input type="text" name="username"/> <br> 17 <input type="text" name="password"/> <br> 18 <input type="hidden" value="${token }" name="reqtoken"/> <br> <!-- 作为hidden提交 --> 19 <input type="submit" value="提交"/> <br> 20 </form> 21 </body> 22 </html>
action代码
1 package com.bjyinfu.struts.actions; 2 3 import java.util.Map; 4 5 import org.apache.struts2.ServletActionContext; 6 7 public class FormSubmit { 8 9 private String username; 10 private String password; 11 private String reqtoken; 12 13 public String getUsername() { 14 return username; 15 } 16 public void setUsername(String username) { 17 this.username = username; 18 } 19 public String getPassword() { 20 return password; 21 } 22 public void setPassword(String password) { 23 this.password = password; 24 } 25 public String getReqtoken() { 26 return reqtoken; 27 } 28 public void setReqtoken(String reqtoken) { 29 this.reqtoken = reqtoken; 30 } 31 public String execute(){ 32 //利用ServletActionContext获取session对象 33 Map<String, Object> session = ServletActionContext.getContext().getSession(); 34 //从session中获取上一次提交表单时,session中的令牌 35 Long lastTokenInSession = (Long) session.get("lastToken"); 36 Long tokenInForm=Long.parseLong(reqtoken); 37 /** 38 * 长整型不能用双等号进行比较 39 * tokenInForm!=null 是防止用户直接打开本servlet页面。 40 * LasttokenInSession是空,说明是第一次提交 ,和上一次不想等则说明不是重复提交 41 */ 42 if(tokenInForm!=null && (lastTokenInSession==null || !lastTokenInSession.equals(tokenInForm))){ 43 //第一次提交和多次非重复提交 44 session.remove("token"); 45 //将表单中的新令牌放到session中 46 session.put("lastToken", tokenInForm); 47 return "success"; 48 }else{ 49 return "fail"; 50 } 51 } 52 }
手工方法二:第一次提交之后,判断这两个token是否一样,是一样则通过,并且清除session 中的 token,这样就能防止返回之后再次提交,因为返回的时候点击提交读取的是缓存,但是session已经没有这个token了。
jsp代码:
1 package com.bjyinfu.struts.actions; 2 3 import java.util.Map; 4 5 import org.apache.struts2.ServletActionContext; 6 7 public class FormSubmit { 8 9 private String username; 10 private String password; 11 private String reqtoken; 12 13 public String getUsername() { 14 return username; 15 } 16 public void setUsername(String username) { 17 this.username = username; 18 } 19 public String getPassword() { 20 return password; 21 } 22 public void setPassword(String password) { 23 this.password = password; 24 } 25 public String getReqtoken() { 26 return reqtoken; 27 } 28 public void setReqtoken(String reqtoken) { 29 this.reqtoken = reqtoken; 30 } 31 public String execute(){ 32 //利用ServletActionContext获取session对象 33 Map<String, Object> session = ServletActionContext.getContext().getSession(); 34 //获取session中的令牌 35 Long TokenInSession = (Long) session.get("token"); 36 //获取表单中的令牌 37 Long tokenInForm=Long.parseLong(reqtoken); 38 /** 39 * 表单中的令牌和session中的令牌不能为空,并且session中的令牌要与表单中的令牌相同才能正常提交 40 * 第一次提交完之后再次刷新,也不会实现二次提交, 41 * 浏览器回退到表单页后再次提交,也不会实现二次提交(回退提交可以先清空浏览器缓存,这样就可以让用户回退去修改信息,实现正常提交了) 42 */ 43 if(tokenInForm!=null && TokenInSession!=null && TokenInSession.equals(tokenInForm)){ 44 //第一次提交和多次非重复提交 45 session.remove("token"); 46 return "success"; 47 }else{ 48 System.out.println("返回并刷新界面"); 49 return "fail"; 50 } 51 } 52 }
在表单页实现清空浏览器缓存,可参考此随笔。