S8.1_Struts2_Interceptor 拦截器的原理 拦截器与过滤器的区别 自定义拦截器 拦截器防止表单重复提交

来源:互联网 发布:知乎 匿名用户 编辑:程序博客网 时间:2024/06/05 18:10
拦截器(Interceptor)

拦截器的原理
拦截器与过滤器的区别
自定义拦截器
拦截器防止表单重复提交


拦截器是struts2框架的核心,struts2中包括解析请求参数,将请求参数赋值给action属性,
执行数据校验,文件上传等工作都是通过拦截器实现的,struts2设计的灵巧性,更大程度地得益于
拦截器设计,当需要扩展struts2功能时,只需要提供对应拦截器,并将它配置在struts2容器中即可;

如果不需要该功能时,也只需要取消该拦截器的配置即可。


struts2内置了大量的拦截器,这些拦截器以name-class对的形式配置在struts-default.xml文件中,其中
name是拦截器的名字,就是以后使用该拦截器的唯一标识;class则指定了该拦截器的实现类,如果我们定义的
package继承了struts2的默认struts-default包,则可以自由使用下面定义的拦截器,否则必须自己定义这些拦截器。

常见的内置拦截器
1. chain:          两个action服务器内部跳转执行的一个拦截器
2. execAndWait:    当执行一个动作在后台执行的时间比较长,在等待的过程中它会转向的去显示一个等待页面
3. fileUpload:     文件上传时执行的一个拦截器
4. timer:          某一个动作执行时间的拦截器
5. token:          解决表单重复提交要使用的一个拦截器
6. token-session   解决表单重复提交要使用的一个拦截器

拦截器与过滤器的区别
1. 拦截器拦截的是动作(action),过滤器过滤的是请求(request),一般映射为/*,表示过滤所有请求;
2. 拦截器是基于java反射机制的,而过滤器是基于函数回调的;
3. 过滤器依赖于servlet容器,而拦截器不依赖于servlet容器;
4. 拦截器可以访问Action上下文,值栈里的对象,而过滤器不能;
5. 在action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次;


自定义拦截器
Interceptor              不推荐使用这个接口
AbstractInterceptor      推荐继承这个抽象类,它也实现了Interceptor这个接口,继承后只需要实现intercept这个抽象方法即可;对所有方法进行拦截
MethodFilterInterceptor  推荐继承MethodFilterInterceptor类,它继承了AbstractInterceptor类;对指定方法进行拦截

我们接下来将要创建的项目目录结构如下:

由项目S4.5_Struts2_ActionAdvance(如何下载它)复制生成一个新项目S8.1_Struts2_Interceptor

自定义拦截器如何继承AbstractInterceptor这个抽象类实现:

第1步:新建net.nw.interceptor包,并在该包内新建TestInterceptor1.java自定义拦截器类
package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class TestInterceptor1 extends AbstractInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       System.out.println("TestIntercptor1拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor1拦截之后执行的动作...");
       return result;
    }
}

第2步:在src/struts.xml配置文件中找到父包default,配置自定义拦截器testInterceptor1 :

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true" ></constant>
    <constant name="struts.i18n.encoding" value="utf-8" ></constant>
     <!-- 父包default -->
     <package name="default" namespace="" extends="struts-default">
        <interceptors>
            <interceptor name="testInterceptor1" class="net.nw.interceptor.TestInterceptor1" />
            <interceptor-stack name="mydefaultStack">
                <interceptor-ref name="testInterceptor1"></interceptor-ref>
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <global-results>
            <result name="login_success">/login_success.jsp</result>
            <result name="login_failure">/login_failure.jsp</result>
        </global-results>
        <action name="exit">
            <result>/login.jsp</result>
        </action>
     </package>
    <package name="error" namespace="" extends="default">
        <action name="*">
            <result>/error.jsp</result>
        </action>
     </package>
    <!-- 子包user继承于父包default -->
    <package name="user" namespace="/user" extends="default">
        <global-results>
            <result>/user/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.UserAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
    <!-- 子包admin继承于父包default -->
    <package name="admin" namespace="/admin" extends="default">
        <global-results>
            <result>/admin/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.AdminAction">
        <result>/admin/admin.jsp</result>
        </action>
        <action name="admin" class="net.nw.action.AdminAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
</struts>

第3步:在src/struts.xml配置文件中找到子包user,将自定义拦截器的拦截器堆栈 mydefaultStack应用到user包中名称为login的action中:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true" ></constant>
    <constant name="struts.i18n.encoding" value="utf-8" ></constant>
     <!-- 父包default -->
     <package name="default" namespace="" extends="struts-default">
        <interceptors>
            <interceptor name="testInterceptor1" class="net.nw.interceptor.TestInterceptor1" />
            <interceptor-stack name="mydefaultStack">
                <interceptor-ref name="testInterceptor1"></interceptor-ref>
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <global-results>
            <result name="login_success">/login_success.jsp</result>
            <result name="login_failure">/login_failure.jsp</result>
        </global-results>
        <action name="exit">
            <result>/login.jsp</result>
        </action>
     </package>
    <package name="error" namespace="" extends="default">
        <action name="*">
            <result>/error.jsp</result>
        </action>
     </package>
    <!-- 子包user继承于父包default -->
    <package name="user" namespace="/user" extends="default">
        <global-results>
            <result>/user/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.UserAction">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
    <!-- 子包admin继承于父包default -->
    <package name="admin" namespace="/admin" extends="default">
        <global-results>
            <result>/admin/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.AdminAction">
        <result>/admin/admin.jsp</result>
        </action>
        <action name="admin" class="net.nw.action.AdminAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
</struts>


第4步:发布运行程序,测试效果截图如下:


第5步:为测试拦截效果,找到net.nw.action包下名称为UserAction.java的动作类,在execute()方法中插入代码System.out.println("普通用户登录 ...");

这样就能看到自定义拦截器的拦截效果了,再次发布运行程序,测试效果截图如下:


接下来演示在一个自定义堆栈<interceptor-stack name="mydefaultStack">一共定义3个拦截器,看一下它们是按照怎样的顺序执行?

第1步:找到net.nw.interceptor包,在该包内新建TestInterceptor2.java自定义拦截器类

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class TestInterceptor2 extends AbstractInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       System.out.println("TestIntercptor2拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor2拦截之后执行的动作...");
       return result;
    }
}

第2步:找到net.nw.interceptor包,在该包内新建TestInterceptor3.java自定义拦截器类
package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class TestInterceptor2 extends AbstractInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       System.out.println("TestIntercptor3拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor3拦截之后执行的动作...");
       return result;
    }
}
第3步:在一个自定义堆栈<interceptor-stack name="mydefaultStack">里一共定义3个拦截器

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true" ></constant>
    <constant name="struts.i18n.encoding" value="utf-8" ></constant>
     <!-- 父包default -->
     <package name="default" namespace="" extends="struts-default">
        <interceptors>
            <interceptor name="testInterceptor1" class="net.nw.interceptor.TestInterceptor1" />
            <interceptor name="testInterceptor2" class="net.nw.interceptor.TestInterceptor2" />
            <interceptor name="testInterceptor3" class="net.nw.interceptor.TestInterceptor3" />

            <interceptor-stack name="mydefaultStack">
                <interceptor-ref name="testInterceptor1"></interceptor-ref>
                <interceptor-ref name="testInterceptor2"></interceptor-ref>
                <interceptor-ref name="testInterceptor3"></interceptor-ref>

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

        <global-results>
            <result name="login_success">/login_success.jsp</result>
            <result name="login_failure">/login_failure.jsp</result>
        </global-results>
        <action name="exit">
            <result>/login.jsp</result>
        </action>
     </package>
    <package name="error" namespace="" extends="default">
        <action name="*">
            <result>/error.jsp</result>
        </action>
     </package>
    <!-- 子包user继承于父包default -->
    <package name="user" namespace="/user" extends="default">
        <global-results>
            <result>/user/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.UserAction">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
    <!-- 子包admin继承于父包default -->
    <package name="admin" namespace="/admin" extends="default">
        <global-results>
            <result>/admin/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.AdminAction">
        <result>/admin/admin.jsp</result>
        </action>
        <action name="admin" class="net.nw.action.AdminAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
</struts>

第4步:发布运行程序,测试效果截图如下:

自定义拦截器如何继承MethodFilterInterceptor 这个类实现:

第1步:在src/struts.xml配置文件中找到子包user,将自定义拦截器<interceptor-ref name="mydefaultStack"></interceptor-ref>添加插入到name="*_*"的action中

接着上面的范例代码继续,其它地方代码保持不变,只插入红色字体标识的代码 --------- 这样增删改方法的执行都会被拦截:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true" ></constant>
    <constant name="struts.i18n.encoding" value="utf-8" ></constant>
     <!-- 父包default -->
     <package name="default" namespace="" extends="struts-default">
        <interceptors>
            <interceptor name="testInterceptor1" class="net.nw.interceptor.TestInterceptor1" />
            <interceptor name="testInterceptor2" class="net.nw.interceptor.TestInterceptor2" />
            <interceptor name="testInterceptor3" class="net.nw.interceptor.TestInterceptor3" />
            <interceptor-stack name="mydefaultStack">
                <interceptor-ref name="testInterceptor1"></interceptor-ref>
                <interceptor-ref name="testInterceptor2"></interceptor-ref>
                <interceptor-ref name="testInterceptor3"></interceptor-ref>
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <global-results>
            <result name="login_success">/login_success.jsp</result>
            <result name="login_failure">/login_failure.jsp</result>
        </global-results>
        <action name="exit">
            <result>/login.jsp</result>
        </action>
     </package>
    <package name="error" namespace="" extends="default">
        <action name="*">
            <result>/error.jsp</result>
        </action>
     </package>
    <!-- 子包user继承于父包default -->
    <package name="user" namespace="/user" extends="default">
        <global-results>
            <result>/user/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.UserAction">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
    </package>
    <!-- 子包admin继承于父包default -->
    <package name="admin" namespace="/admin" extends="default">
        <global-results>
            <result>/admin/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.AdminAction">
        <result>/admin/admin.jsp</result>
        </action>
        <action name="admin" class="net.nw.action.AdminAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
</struts>

第2步:发布运行程序,测试效果截图如下:



接着上面演示只拦截添加和删除方法的实验步骤:

第1步:在net.nw.interceptor包下,让TestInterceptor1拦截类继承 MethodFilterInterceptor类,TestInterceptor1.java实现的代码如下:

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class TestInterceptor1 extends MethodFilterInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String doIntercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       System.out.println("TestIntercptor1拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor1拦截之后invocation.invoke()返回的值:"+result);
       System.out.println("TestIntercptor1拦截之后执行的动作...");
       return result;
    }
}

第2步:在net.nw.interceptor包下,让TestInterceptor2拦截类继承 MethodFilterInterceptor类,TestInterceptor2.java实现的代码如下:

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class TestInterceptor2 extends MethodFilterInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String doIntercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       System.out.println("TestIntercptor2拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor2拦截之后invocation.invoke()返回的值:"+result);
       System.out.println("TestIntercptor2拦截之后执行的动作...");
       return result;
    }
}

第3步:在net.nw.interceptor包下,让TestInterceptor3拦截类继承 MethodFilterInterceptor类,TestInterceptor3.java实现的代码如下:

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class TestInterceptor3 extends MethodFilterInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String doIntercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       System.out.println("TestIntercptor3拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor3拦截之后invocation.invoke()返回的值:"+result);
       System.out.println("TestIntercptor3拦截之后执行的动作...");
       return result;
    }
}

第4步:在 src/struts.xml配置文件中找到父包default,在3个自定义interceptor-ref中都配置<param name="excludeMethods">delete</param>,

表示除删除方法之外的方法都会被拦截:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true" ></constant>
    <constant name="struts.i18n.encoding" value="utf-8" ></constant>
     <!-- 父包default -->
     <package name="default" namespace="" extends="struts-default">
        <interceptors>
            <interceptor name="testInterceptor1" class="net.nw.interceptor.TestInterceptor1" />
            <interceptor name="testInterceptor2" class="net.nw.interceptor.TestInterceptor2" />
            <interceptor name="testInterceptor3" class="net.nw.interceptor.TestInterceptor3" />
            <interceptor-stack name="mydefaultStack">
                <interceptor-ref name="testInterceptor1">
                    <param name="excludeMethods">delete</param>
                </interceptor-ref>
                <interceptor-ref name="testInterceptor2">
                    <param name="excludeMethods">delete</param>
                </interceptor-ref>
                <interceptor-ref name="testInterceptor3">
                    <param name="excludeMethods">delete</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>

        <global-results>
            <result name="login_success">/login_success.jsp</result>
            <result name="login_failure">/login_failure.jsp</result>
        </global-results>
        <action name="exit">
            <result>/login.jsp</result>
        </action>
     </package>
    <package name="error" namespace="" extends="default">
        <action name="*">
            <result>/error.jsp</result>
        </action>
     </package>
    <!-- 子包user继承于父包default -->
    <package name="user" namespace="/user" extends="default">
        <global-results>
            <result>/user/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.UserAction">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
    </package>
    <!-- 子包admin继承于父包default -->
    <package name="admin" namespace="/admin" extends="default">
        <global-results>
            <result>/admin/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.AdminAction">
        <result>/admin/admin.jsp</result>
        </action>
        <action name="admin" class="net.nw.action.AdminAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
</struts>

第5步:发布运行程序,测试效果截图如下:

上面这种配置拦截效果与下两种配置拦截效果一样:


接下来介绍使用PreResultListener在返回结果之前执行一个方法(执行插入点在 执行action之后与excute()方法内部返回result之前的位置),实验步骤如下:

第1步:在net.nw.interceptor包下创建一个名称为BeforeResultInterceptor.java的类,就翻译成前置返回结果监听器吧,并实现PreResultListener接口:

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.PreResultListener;
public class BeforeResultInterceptor implements PreResultListener{
    @Override
    public void beforeResult(ActionInvocation invocation, String result) {
        // TODO Auto-generated method stub
        System.out.println("beforeResult方法第2个输入参数的值等于:"+result);
        System.out.println("返回结果集之前执行beforeResult()方法......");
    }
}

第2步:将实现的前置返回结果监听器添加到自定义的拦截器(TestInterceptor1)里,实现一个像拦截器的功能,因而不需要再添加什么配置到struts.xml文件当中,

但它(BeforeResultInterceptor)不是拦截器,TestInterceptor1代码如下:

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class TestInterceptor1 extends MethodFilterInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String doIntercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       invocation.addPreResultListener(new BeforeResultInterceptor());
       System.out.println("TestIntercptor1拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor1拦截之后invocation.invoke()返回的值:"+result);
       System.out.println("TestIntercptor1拦截之后执行的动作...");
       return result;
    }
}

第3步:将实现的前置返回结果监听器添加到自定义的拦截器(TestInterceptor1)里,实现一个像拦截器的功能,因而不需要再添加什么配置到struts.xml文件当中,

但它(BeforeResultInterceptor)不是拦截器,TestInterceptor2代码如下:

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class TestInterceptor2 extends MethodFilterInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String doIntercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       invocation.addPreResultListener(new BeforeResultInterceptor());
       System.out.println("TestIntercptor2拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor2拦截之后invocation.invoke()返回的值:"+result);
       System.out.println("TestIntercptor2拦截之后执行的动作...");
       return result;
    }
}

第4步:将实现的前置返回结果监听器添加到自定义的拦截器(TestInterceptor1)里,实现一个像拦截器的功能,因而不需要再添加什么配置到struts.xml文件当中,

但它(BeforeResultInterceptor)不是拦截器,TestInterceptor3代码如下:

package net.nw.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class TestInterceptor3 extends MethodFilterInterceptor{
    private static final long serialVersionUID = 1L;
    @Override
    public String doIntercept(ActionInvocation invocation) throws Exception {
        // TODO Auto-generated method stub
       invocation.addPreResultListener(new BeforeResultInterceptor());
       System.out.println("TestIntercptor3拦截之前执行的动作...");
       String result = invocation.invoke();
       System.out.println("TestIntercptor3拦截之后invocation.invoke()返回的值:"+result);
       System.out.println("TestIntercptor3拦截之后执行的动作...");
       return result;
    }
}

第5步:发布运行程序,测试效果截图如下:



使用拦截器解防止表单重复提交
<!-- 显示重复提交出错的页面 -->
<interceptor-ref name="token"></interceptor-ref>
<!-- 显示提交成功的页面,但是并不执行重复提交的动作 -->
<interceptor-ref name="tokenSession"></interceptor-ref>

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


第1种防止表单重复提交的办法是使用token

第1步:创建一个重复提交出错的页面,名称为token_error.jsp,页面代码如下:

<%@ page language="java" import="java.util.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>重复提交提示</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>
  <body>
    <center>
        <h1>重复提交提示</h1>
        <hr>
        请您不要重复提交!
    </center>
  </body>
</html>

第2步:在配置文件struts.xml中找到父包default,然后在拦截器末尾找到默认的堆栈defaultStack拦截器

在默认堆栈拦截器<interceptor-ref name="defaultStack"></interceptor-ref>上面的位置

配置显示重复提交出错的页面的token拦截器

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

还是在父包default内部的下面的<global-results>全局结果集,然后在<global-results></global-results> 末尾的位置

配置<result name="invalid.token">/token_error.jsp</result> ,此步干的事情红色字体的代码已被标识出来了,struts.xml文件的代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true" ></constant>
    <constant name="struts.i18n.encoding" value="utf-8" ></constant>
     <!-- 父包default -->
     <package name="default" namespace="" extends="struts-default">
        <interceptors>
            <interceptor name="testInterceptor1" class="net.nw.interceptor.TestInterceptor1" />
            <interceptor name="testInterceptor2" class="net.nw.interceptor.TestInterceptor2" />
            <interceptor name="testInterceptor3" class="net.nw.interceptor.TestInterceptor3" />
            <interceptor-stack name="mydefaultStack">
                <interceptor-ref name="testInterceptor1">
                    
                </interceptor-ref>
                <interceptor-ref name="testInterceptor2">
                    
                </interceptor-ref>
                <interceptor-ref name="testInterceptor3">
                    
                </interceptor-ref>
            <!-- 显示重复提交出错的页面 -->
            <interceptor-ref name="token"></interceptor-ref>

                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        <global-results>
            <result name="login_success">/login_success.jsp</result>
            <result name="login_failure">/login_failure.jsp</result>
            <result name="invalid.token">/token_error.jsp</result>
        </global-results>
        <action name="exit">
            <result>/login.jsp</result>
        </action>
     </package>
    <package name="error" namespace="" extends="default">
        <action name="*">
            <result>/error.jsp</result>
        </action>
     </package>
    <!-- 子包user继承于父包default -->
    <package name="user" namespace="/user" extends="default">
        <global-results>
            <result>/user/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.UserAction">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
            <interceptor-ref name="mydefaultStack">
                <param name="testInterceptor1.includeMethods">add,modify</param>
                <param name="testInterceptor2.includeMethods">add,modify</param>
                <param name="testInterceptor3.includeMethods">add,modify</param>
            </interceptor-ref>
        </action>
    </package>
    <!-- 子包admin继承于父包default -->
    <package name="admin" namespace="/admin" extends="default">
        <global-results>
            <result>/admin/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.AdminAction">
        <result>/admin/admin.jsp</result>
        </action>
        <action name="admin" class="net.nw.action.AdminAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
</struts>

第3步:我们需要在WebRoot/login.jsp页面生成一个随机令牌,注意红色字体标识的代码,login.jsp页面代码如下:

<%@ page language="java" import="java.util.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>系统登录</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>
  <body>
        <center>
        <h1>系统登录</h1>
        <hr>
        <form name="loginForm" action="<%=path%>/user/login.action" method="post" >
            用户名称:<input type="text" name="username"/><br>
            用户密码:<input type="password" name="password"/><br>
                    <input type="submit" value="登录"/><br>
                    <br>
                    <br><a href="<%=path%>/admin/login.action">管理员登录</a>
                    <s:token></s:token><!-- 生成一个随机令牌 -->
        </form>
    </center>
  </body>
</html>

第4步:发布运行程序,测试效果截图如下:

第2种防止表单重复提交办法是使用tokenSession,其它的都不变,把struts.xml配置文件中的token改成tokenSession即可,差别是不会跳转到提示重复提交这一页

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true" ></constant>
    <constant name="struts.i18n.encoding" value="utf-8" ></constant>
     <!-- 父包default -->
     <package name="default" namespace="" extends="struts-default">
        <interceptors>
            <interceptor name="testInterceptor1" class="net.nw.interceptor.TestInterceptor1" />
            <interceptor name="testInterceptor2" class="net.nw.interceptor.TestInterceptor2" />
            <interceptor name="testInterceptor3" class="net.nw.interceptor.TestInterceptor3" />
            <interceptor-stack name="mydefaultStack">
                <interceptor-ref name="testInterceptor1">
                    
                </interceptor-ref>
                <interceptor-ref name="testInterceptor2">
                    
                </interceptor-ref>
                <interceptor-ref name="testInterceptor3">
                    
                </interceptor-ref>
           <!-- 显示提交成功的页面,但是并不执行重复提交的动作 -->
           <interceptor-ref name="tokenSession"></interceptor-ref>

                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        <global-results>
            <result name="login_success">/login_success.jsp</result>
            <result name="login_failure">/login_failure.jsp</result>
            <result name="invalid.token">/token_error.jsp</result>
        </global-results>
        <action name="exit">
            <result>/login.jsp</result>
        </action>
     </package>
    <package name="error" namespace="" extends="default">
        <action name="*">
            <result>/error.jsp</result>
        </action>
     </package>
    <!-- 子包user继承于父包default -->
    <package name="user" namespace="/user" extends="default">
        <global-results>
            <result>/user/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.UserAction">
            <interceptor-ref name="mydefaultStack"></interceptor-ref>
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
            <interceptor-ref name="mydefaultStack">
                <param name="testInterceptor1.includeMethods">add,modify</param>
                <param name="testInterceptor2.includeMethods">add,modify</param>
                <param name="testInterceptor3.includeMethods">add,modify</param>
            </interceptor-ref>
        </action>
    </package>
    <!-- 子包admin继承于父包default -->
    <package name="admin" namespace="/admin" extends="default">
        <global-results>
            <result>/admin/result.jsp</result>
        </global-results>
        <action name="login" class="net.nw.action.AdminAction">
        <result>/admin/admin.jsp</result>
        </action>
        <action name="admin" class="net.nw.action.AdminAction">
        </action>
        <action name="*_*" class="net.nw.action.{1}Action" method="{2}">
        </action>
    </package>
</struts>


本项目的下载地址:

0 0