Struts2 TokenInterceptor防止表单重复提交

TokenInterceptor防止表单重复提交。

由于某些原因,用户在进行类似表单提交的操作后,以为表单未被提交,会进行多次的重复提交。为了避免用户多次提交给服务器带来负荷。我们会对表单提交这样的操作进行一些处理,以告诉用户不要重复提交。下面我们建立struts2token项目,使用struts2的token拦截器来实现此案例。
步骤一,编写login.jsp页面,内容如下:
<%@ page language="java" pageEncoding="UTF-8"%>

<%@ taglib uri="/struts-tags"prefix="s" %>

<html>

    <body>

       <form action="<%=request.getContextPath()%>/login.action">

           姓名:<input type="text" name="username"><br>

           密码:<input type="password"name="password"><br>

           <input type="submit"value="登录">

           <s:token></s:token>

       </form>

    </body>

</html>
说明,此登录页面中的关键技术就是使用了标签库中的<s:token></s:token>标签,它的作用就是在用户访问此页面时会生成一个sessionId,在提交时会服务器会据此验证表单是否已提交。“To set a token in your form, you should use the token tag.This tag is required and must be used in the forms that submit to actionsprotected by this interceptor”,这句话的大概意思就是我们必须要在提交的表单中使用这个token tag,这样提交到的Action便能配置TokenInterceptor拦截器验证表单是否重复提交。
步骤二,编写LoginAction,主要代码如下:
package com.asm;
publicclass LoginAction extends ActionSupport {

    public String execute() throws Exception {

       System.out.println("---->执行execute方法...");

       returnSUCCESS;

    }

}
步骤三,struts.xml主要配置内容如下:
<struts>

    <package name="tokenTest" extends="struts-default">

       <action name="login" class="com.asm.LoginAction">

           <result name="success">/success.jsp</result>

           <result name="invalid.token">/subError.jsp</result>

           <interceptor-ref name="token"></interceptor-ref>

           <interceptor-ref name="defaultStack"></interceptor-ref>

       </action>

    </package>

</struts>

说明:在此Action下,我们配置了token拦截器,另注意到在此Action下我们还配置了一个“invalid.token”result,因为“This interceptor uses a fairlyprimitive technique for when an invalid token is found: it returns the result invalid.token,which can be mapped in your action configuration”。它的大概意思就是:提交时服务器如果根据token标签产生的sessionId判断出表单已提交,它则返回invalid.token指向的视图。比如这里,如果重复提交则会转到.../subError.jsp中去。另不要忘记了引入默认的拦截器栈。补充:关于token拦截器更多细节可以访问org.apache.struts2.interceptor.TokenInterceptor类的api说明。
步骤四,编写配置中所用到jsp页面,这些页面编写简单,在此省去。
步骤五、发布测试,请注意访问login.jsp页面时,查看源文件时会发现增加了两个隐藏域信息。
步骤六、更换拦截器:我们还可以使用tokenSession拦截器,它的功能比上面的增强,它能保证持有相同sessionId的并发请求等待第一个完成之后才能被提交处理,但是它返回的是action执行后的result.接着上例,我们只需要在配置中作如下修改:把上面的token拦截器改成<interceptor-ref name="tokenSession"></interceptor-ref> 即可。随后便可以测试,测试时会发现如果我们重复提交,它总是返回到上一次的success.jsp页面,但是它并不是经过LoginAction中的execute处理后返回(我们System.out.print语句在重复提交时并未打印出来),而是此拦截器判断出是重复后直接返回上一次提交转向的页面。



/*
 * 工程名: portalMS
 * 包     名: com.xxxx.dhm.portalMS.base.web.interceptor
 * 文 件名: ExTokenInterceptor.java
 * 版      权: Copyright (c) 2009 xxxxAll Rights Reserved.
 * 描      述: 防止重复提交的拦截器
 * 修 改 人:
 * 修改时间:
 * 跟踪单号:
 * 修改单号:
 * 修改内容:
 */
package com.xxxxxx.dhm.portalMS.base.web.interceptor;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.TokenInterceptor;
import org.apache.struts2.util.TokenHelper;

import com.xxxxxx.dhm.portalMS.common.Constants;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ValidationAware;
import com.opensymphony.xwork2.util.LocalizedTextUtil;

/**
 *
 * 防止重复提交的拦截器,通过扩展了struts2的重复提交的拦截器。
 * 该拦截器的使用如下:
 *     1、不进行重复提交校验,那么该拦截器将不做任何拦截操作。
 *  2、如果进行重复提交校验,需要做的操作如下:
 *     1)、在jsp页面中form中加入:<s:token />,那么就进行重复提交的校验操作。
 *     2)、校验的规则是这样的,首先会重http请求参数中获取该token的值,然后再从
 *     session中获取同名的token值,然后两个值进行比较。
 *     3)、如果两个一样,则校验通过,否则struts2将会将跳转到“input”配置的页面去。
 *     
 *
 * @author  
 * @version  
 * @since  
 */
public class ExTokenInterceptor extends TokenInterceptor {

    /**
     * 序列化ID
     */
    private static final long serialVersionUID = -6688464557536335754L;
    /**日志记录对象*/
    private static final Logger logger = Logger.getLogger( ExTokenInterceptor.class );
    
    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Intercepting invocation to check for valid transaction token.");
        }

        HttpSession session = ServletActionContext.getRequest().getSession(true);

        synchronized (session) {
            String tokenName = getTokenName();
            if( tokenName == null ) {
                return handleValidToken(invocation);
            }
            
            if (!validToken()) {
                return handleInvalidToken(invocation);
            } else {
                return handleValidToken(invocation);
            }
        }
    }
    
    @Override
    protected String handleInvalidToken(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();
        String errorMessage = LocalizedTextUtil.findText(this.getClass(), Constants.MSG_PORTALMS+"struts.messages.invalid.token",
                invocation.getInvocationContext().getLocale(),
                "The form has already been processed or no token was supplied, please try again.", new Object[0]);

        if (action instanceof ValidationAware) {
            ((ValidationAware) action).addActionError(errorMessage);
        } else {
            logger.warn(errorMessage);
        }

        return Action.INPUT;
    }
    
    public static boolean validToken() {
        String tokenName = TokenHelper.getTokenName();
        if ( tokenName == null ) {
            return true;
        }

        String token = TokenHelper.getToken(tokenName);

        if (token == null) {
            return false;
        }

        Map<String, Object> session = ActionContext.getContext().getSession();
        String sessionToken = (String) session.get(tokenName);

        if (!token.equals(sessionToken)) {
            logger.warn(LocalizedTextUtil.findText(TokenHelper.class, "struts.internal.invalid.token", ActionContext.getContext().getLocale(), "Form token {0} does not match the session token {1}.", new Object[]{
                    token, sessionToken
            }));
            
            return false;
        }
        
        // remove the token so it won't be used again
        session.remove(tokenName);
        return true;
    }
    
    public static String getTokenName() {
        Map<String, Object> params = ActionContext.getContext().getParameters();

        if (!params.containsKey( "struts.token.name" )) {
            return null;
        }

        String[] tokenNames = (String[]) params.get("struts.token.name");
        String tokenName;

        if ((tokenNames == null) || (tokenNames.length < 1)) {
            return null;
        }

        tokenName = tokenNames[0];

        return tokenName;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值