• 使用struts的同步令牌避免form的重复提交


    struts1避免重复提交

    一、使用方法

    1、  假如你要提交的页面为toSubmit.jsp;

    2、  在打开toSubmit.jsp的Action1中加入:saveToken(request),例如

    public ActionForward execute(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
        throws Exception {
            //生成同步令牌
            saveToken(request);    
            return mapping.findForward("toSubmit");
    }

    3、  在提交toSubmit.jsp的Action2中加入:isTokenValid(request, true),例如:

    public ActionForward execute(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
        throws Exception {
            // 验证同步令牌
            if (isTokenValid(request, true)) { 
                //执行提交操作 
            }else {
                // 重复提交        
                return mapping.findForward("Error");
            }
    }

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

    二、基本原理

    第一步、在session中放入同步令牌

    在Action1中加入了saveToken(request)的方法后,调用TokenProcessor类的saveToken方法如下:

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

    很明显在session中放入了同步令牌,名称为Globals.TRANSACTION_TOKEN_KEY。

    第二步、在页面创建hidden元素

    当应用服务器初始化toSubmit.jsp页面遇到标签<html:form>时,便会调用struts的FormTag类,其中有一个方法:

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

    其意为:当检测到session中的Globals.TRANSACTION_TOKEN_KEY不为空时,在toSubmit.jsp页面创建元素:

    <input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="">

    名称为:org.apache.struts.taglib.html.TOKEN就是Constants.TOKEN_KEY;

    值为:session中的Globals.TRANSACTION_TOKEN_KEY的值,即为同步令牌值。

    第三步、验证同步令牌

    在Action2中加入isTokenValid方法,实际上是调用TokenProcessor类的isTokenValid方法如下:

    public synchronized boolean isTokenValid(
        HttpServletRequest request,
        boolean reset) {
     
        // Retrieve the current session for this request
        HttpSession session = request.getSession(false);
        if (session == null) {
            return false;
        }
     
        // Retrieve the transaction token from this session, and
        // reset it if requested
        String saved = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
        if (saved == null) {
            return false;
        }
     
        if (reset) {
            this.resetToken(request);
        }
     
        // Retrieve the transaction token included in this request
        String token = request.getParameter(Constants.TOKEN_KEY);
        if (token == null) {
            return false;
        }
     
        return saved.equals(token);
    }

    它首先取得session中的令牌值,然后resetToken,再从页面hidden元素取来令牌值,进行比较,如果相等则为第一次,不等则为重复提交。

    其中resetToken方法如下:

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

    struts2避免重复提交

    struts2使用拦截器来检查表单是否重复提交,它采用同步令牌的方式来实现对表单重复提交的判断。

    首先需要在表单中使用

    <s:token name="user.token"></s:token>

    <s:token>标签创建一个新的令牌值,并用你所指定的令牌名把令牌保存到session中。而这个令牌值是随即产生的经过加密的字符序列,不会重复。struts2使用拦截器来检查表单是否重复提交,它采用同步令牌的方式来实现对表单重复提交的判断。

    其次需要为action配置TokenInterceptor或者TokenSessionStoreInterceptor拦截器。这两个拦截器都已经在struts-default.xml中定义,但没有包含在defaultStack拦截器栈中。

    1、使用TokenInterceptor在action中配置拦截器和在重复提交时,将要请求导向的结果视图。

    <action name="register" class="com.zhaosoft.action.RegisterAction">
          <!-- 配置异常映射,当RegisterAction抛出Exception异常时,向用户显示error.jsp页面 -->
          <exception-mapping result="error" exception="java.lang.Exception"/>
          <result name="invalid.token">/WEB-INF/pages/register.jsp</result>
          <result name="input">/WEB-INF/pages/register.jsp</result>
          <result name="success">/WEB-INF/pages/success.jsp</result>
          <result name="error">/WEB-INF/pages/error.jsp</result>
          <interceptor-ref name="defaultStack">
                <param name="workflow.excludeMethods">default</param>
          </interceptor-ref>
          <interceptor-ref name="token">
             <param name="excludeMethods">default</param>
          </interceptor-ref>
    </action>

    注:excludeMethods指定要排除的方法。

    在register.jsp页面中添加action级别的错误信息显示的标签:<s:actionerror/>

    在form中添加<s:token>标签:<s:token name="user.token"></s:token>

    最好为在资源文件中设置键struts.messages.invalid.token的本地化消息。

    struts.messages.invalid.token=您已经提交了表单,请不要重复提交。

     

    2、TokenSessionStoreInterceptor:

    使用TokenSessionStoreInterceptor拦截器同样能避免重复提交,TokenSessionStoreInterceptor集成自TokenInterceptor。与上面的TokenInterceptor区别在于使用TokenSessionStoreInterceptor将不会输出任何错误信息。如果token无效,请求被导向到invalid.token结果码映射的视图。

    配置如下:

    <interceptor-ref name="tokenSession">
             <param name="excludeMethods">default</param>
    </interceptor-ref>
  • 相关阅读:
    分布式锁相关
    《并发编程的艺术》笔记
    JVM相关总结
    redisTemplate类学习及理解
    wondows 安装 OpenSSH
    centos系统下,mysql常用命令 vofill
    查看centos7服务器信息常用命令 vofill
    封装SQL2005数据库操作的Framework
    C# 获取到当前处于活跃状态的Excel对象(即已经打开的)c#获取打开的excel
    QML Qt Quick Study Note No.2
  • 原文地址:https://www.cnblogs.com/duanxz/p/3639955.html
Copyright © 2020-2023  润新知