使用struts同步令牌机制避免表单的重复提交

来源:互联网 发布:淘宝充值店客户的来源 编辑:程序博客网 时间:2024/05/17 22:10

一、使用方法 

1、假设你要提交的叶面为usermesg.jsp。

2、在打开usermesg.jsp的SaveTokenAction中加入saveToken(request),源码如下:

 

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 SaveToken extends Action {

    
/* forward name="usermesg" path="/usermesg.jsp" */
    
private final static String USERMESG = "usermesg";

    
public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            
throws Exception {
        
         
// 在session中放入同步令牌
        saveToken(request);

        
// TODO process request and return an ActionForward instance, for example:
        
// return mapping.findForward(USERMESG);
        return mapping.findForward(USERMESG);
    }

}

3、在叶面提交的ValidatorAction中加入isTokenValid(request,true)源码如下:

 

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 Validator extends Action {
    
private final static String ERROR="error";
    
private final static String WELCOME="welcome";
    
public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            
throws Exception {
        
         
// 验证同步令牌
        if(isTokenValid(request,true)){
            
return mapping.findForward(WELCOME);
        }
else{
            
return mapping.findForward(ERROR);
        }

        
// TODO process request and return an ActionForward instance, for example:
        
// return mapping.findForward("forward_name");
    }

}

4、  使用注意:usermesg.jsp中的form表单必须使用struts的标签<html:form>。

二、基本原理

1、在session中放入同步令牌。原理:调用TokenProcessor类的saveToken(HttpServletRequest arg0);源码如下:

在sesssion中放入名称为Globale.TRANSACTION_TOKEN_KEY的同步令牌:token

          public synchronized void saveToken(HttpServletRequest request){
            HttpSession session
=request.getSession();
            String token
=generateToken(request);
            
if(token!=null){
               session.setAttribute(Globale.TRANSACTION_TOKEN_KEY,token);
            }

         }

 

2、在打开usermesg.jsp时,应用服务器遇到<html:form>时,便会调用FormTag类的renderToken()方法创建hidden元素。源码如下:

 

protected String renderToken(){
               StringBuffer result
=new StringBuffer();
               HttpSession session
=pageContext.getSession();
               
if(session!=null){
                  String token
=(String)session.getAttribute(Globale.TRANSACTION_TOKEN_KEY);
                  
if(token!=null){
                    result.append(
"<input type="hidden" name="");
                    result.append(Constants.TOKEN_KEY);
                    result.append(
"" value="");
                    result.append(token);
                    
if(this.isXhtml){
                      result.append(
"" />");
                    }
else{
                      result.append(
"" >");
                    }

                  }

               }

               
return result.toStriong();
         }

hidden元素Constants.TOKEN_KEY的值就是session中的名称为Globale.TRANSACTION_TOKEN_KEY的同步令牌值

3、验证同步令牌,原理:调用TokenProcessor的isTokenValid(HttpServletRequest arg0,boolean arg1)源码如下:

 

public synchronized boolean isTokenValid(HttpServletRequest request,boolean reset){
            HttpSession session request.getSession(
false);
            
if(session==nullreturn false;
            
            String saved
=(String)session.getAttribute(Globale.TRANSACTION_TOKEN_KEY);
            
            
if(saved==nullreturn false;
            
            
if(reset) this.resetToken(request);


            String token
=request.getParameter(Constants.TOKEN_KEY);
             
             
if(token==nullreturn false;
             
             
return saved.equals(token);
          }

从叶面取出同步令牌值和session中的进行比较。如果相等则为第一次,不等就是重复提交。前提就是传进来的参数reset必须是true。不然每次都相同。其中resetToken()方法如下:

 


                
public synchronized void resetToken(HttpServletRequest request){
                    HttpSession session request.getSession(
false);
                    
if(session==null){
                      
return;
                    }

                    session.removeAttribute(Globale.TRANSACTION_TOKEN_KEY);
                }

这就是在isTokenValid方法中boolean参数的作用:清除session中的同步令牌,避免重复提交。如果把true改为false 将不会起到避免重复提交的作用