zoukankan      html  css  js  c++  java
  • 令牌机制,防止表单重复提交

    方法一:利用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方法中来实现程序的健壮;

    重复提交时:

    回退到表单中:

     方法二:手动编写令牌机制,利用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 }

    在表单页实现清空浏览器缓存,可参考此随笔

  • 相关阅读:
    博客园cnblogs for win8 托管到GitHub开源
    html5 canvas 画图表
    回文数
    SpringBoot+logback实现按业务输出日志到不同的文件
    Class.forName() 与 ClassLoader.loadClass()的区别
    Easypoi实现单模板生成多页word文档
    普通Java项目中使用Sl4j+Log4j2打印日志
    SpringBoot集成JWT
    Java8_Lambda表达式
    SpringBoot自定义Condition注解
  • 原文地址:https://www.cnblogs.com/lubolin/p/7449235.html
Copyright © 2011-2022 走看看