七、Struts2的拦截器

  通过前面的学习,我们了解到Action才是具体处理用户的业务实现部分,但有的时候我们需要在Action处理业务之前进行有效性校验或数据类型转换,或者在Action处理业务之后进行日志记录或转换等,而且这些操作是低耦合,可自定义动态拆卸的。因此,就引入了Struts2拦截器的概念。

主要内容:

1.什么是Struts拦截器;
2.Struts拦截器的工作原理;
3.如何在struts.xml中配置使用Struts拦截器;
4.Struts2框架所提供的内置拦截器;
5.自定义Struts拦截器。
6.案例:自定义拦截器实现用户登录权限控制

1.什么是Struts拦截器

  拦截器是Struts2框架的重要组成部分,Struts2的很多功能都是构建在拦截器之上的,如数据校验、转换器、国际化等。
作用:它可以动态拦截Action调用的对象,类似于Servlet中的过滤器。
其中,
单个拦截器:可以完成单个功能。
(多个)拦截器链:不同功能的拦截器按照一定的顺序排列在一起形成的链。
拦截器栈:拦截器链组成的集合。

2.Struts拦截器的工作原理;

  Struts2的拦截器是AOP (Aspect-Object-Programming,面向切面编程)的一种实现策略,是可插拔的。它可以任意地组合Action提供的附加功能,而不需要修改Action的代码,开发者只需要提供拦截器的实现类,并将其配置在struts.xml中即可。

拦截器与Action之间的执行顺序

  当用户触发事件,系统会在struts.xml配置文件中寻找对应的Action配置信息时,根据配置信息会先执行附加到Action之上的拦截器,然后再执行Action对象。

3.如何在struts.xml中配置使用Struts拦截器;

  因为拦截器与Action之间处在一种关联关系,因此,我们需要现在struts.xml中声明它们的这种关系,系统才能根据配置信息顺序执行。
  在struts.xml中声明拦截器,使用的是<interceptor>标签

1.配置单个拦截器

<interceptor  name="interceptorName“   class="interceptorClass">
    <param name="paramName">paramValue</param>
</interceptor>
struts.xml中配置单个拦截器

2.配置拦截器栈(多个拦截器)

<interceptors>
    <interceptor-stack name="interceptorStackName">
        <interceptor-ref  name="interceptorName" />
        ...
    </interceptor-stack>
</interceptors>
struts.xml中配置拦截器栈.png

3.配置当前所使用的默认拦截器

<default-interceptor-ref      name="拦截器(栈)的名称"/>
struts.xml中配置默认拦截器

4.Struts2框架所提供的内置拦截器;

  Struts2利用其内建的拦截器其实已经可以完成大部分的操作, 当内置拦截器不能满足时,开发者也可以自己扩展。
  Struts2中内置了许多拦截器,这些拦截器以name-class对的形式配置在struts-default.xml文件中。

name 是拦截器的名称,也就是引用的名字;
class 是指定了该拦截器所对应的实现类。

  在项目中,只要自定义的包继承了Struts2的struts-default包,就可以使用默认包中所定义的这些内建拦截器。在struts-default.xml中,每一个拦截器都具有不同的意义和功能。

在struts-core-2.3.24.jar包中的根目录下找到struts-default.xml文件,打开后找到<interceptors>元素下的内建拦截器和拦截器栈。

5.自定义Struts拦截器。

  在实际的项目开发中,Struts2的内置拦截器可以完成大部分的拦截任务。但是,一些与系统逻辑业务逻辑相关的通用功能(如权限的控制、用户登录控制等),则需要通过自定义拦截器类来实现功能。

方法一:实现Interceptor接口

  在程序开发过程中,如果需要开发自己的拦截器类,就需要直接或间接的实现com.opensymphony.xwork2.interceptor.Interceptor接口。

public interface Interceptor extends Serializable {
        void init();
        void destroy();
        String intercept(ActionInvocation invocation) throws Exception;
}

  如果需要自定义拦截器,只需要实现Interceptor接口的三个方法即可。

  • init()方法在拦截器被创建后会立即被调用, 它在拦截器的生命周期内只被调用一次. 可以在该方法中对相关资源进行必要的初始化。
  • destroy()方法与init()方法相对应,在拦截器实例被销毁之前,将调用该方法来释放和拦截器相关的资源。它在拦截器的生命周期内,也只被调用一次。
  • Intercep()方法是拦截器的核心方法,用来添加真正执行拦截工作的代码,实现具体的拦截操作。它返回一个字符串作为逻辑视图,系统根据返回的字符串跳转到对应的视图资源。每拦截一个动作请求, 该方法就会被调用一次。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过该参数的invoke()方法,将控制权转给下一个拦截器或者转给Action的execute()方法。

方法二:继承AbstractIntercepter类(建议)

  该类实现了Interceptor接口,并且提供了init()方法和destroy()方法的空实现。使用时,可以直接继承该抽象类,而不用实现那些不必要的方法。因此,使用更为简单方便。

public abstract class AbstractInterceptor implements Interceptor {
        public void init() {}
        public void destroy() {}
        public abstract String intercept(ActionInvocation invocation) 
              throws Exception;
}

用法:

  只需继承AbstractInterceptor类,实现interceptor()方法就可以创建自定义拦截器。
  只有当自定义的拦截器需要打开系统资源时,才需要覆盖AbstractInterceptor类的init()方法和destroy()方法。

  • 总结:自定义拦截器的流程
    1.必须实现Interceptor接口或继承AbstractInterceptor类;
    2.需要在Struts.xml中声明自定义的拦截器;
    3.在Struts.xml中的Action中使用拦截器。

6.案例:自定义拦截器实现用户登录权限控制

1.web.xml:引入Struts框架的包,并把Struts框架配置到项目中;
2.login.jsp:用户登录页面
3.User.java:模型实体类(JavaBean),作为传输参数Model;
4.LoginAction:验证登录的业务处理;
5.main.jsp:用户登录成功后,自动跳转到主页;
6.OperationAction:(用户登录后)用户操作的业务处理;
7.PrivilegeInterceptor.java:自定义拦截器,用于拦截没登录的用户。
8.success.jsp:用户操作成功所跳转到的模拟页面;
9.struts.xml:Struts框架的路由配置文件。

1.web.xml

先创建Web项目LoginPower,把Struts框架的包都引入到项目WebContent—WEB-INF—lib中,并把Struts框架配置到项目的web.xml中;

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <welcome-file-list>
    <welcome-file>main.jsp</welcome-file>
  </welcome-file-list>
</web-app>

2.login.jsp

在WebContent目录下新建用户登录页面login.jsp。

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>登录</title>
</head>
<body>
<center>
${requestScope.msg}<br>
    <form action="/LoginPower/login.action" method="post">
        <table>
            <tr>
                <td><label style="text-align: right;">用户名:</label></td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td><label style="text-align: right;">密码:</label></td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td align="right" colspan="2"><input type="submit" value="登录"></td>
            </tr>
        </table>
    </form>
</center>
</body>
</html>

3.User.java

模型实体类(JavaBean),作为传输参数Model;
在项目Java Resource—src中新建一个cn.demo.domain包,用于存放所有的Model,并在里面新建一个JavaBean实体类:User用户类

package cn.demo.domain;
public class User {
    private String username; //用户名
    private String password; //密码
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

4.LoginAction

验证登录的业务处理;
在项目Java Resource—src中新建一个cn.demo.action包,用于存放所有的Action类。在里面新建LoginAction.java用于实现用户登录验证的功能。

package cn.demo.action;

import cn.demo.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class LoginAction extends ActionSupport implements ModelDriven<User> {
    private static final long serialVersionUID = 1L;
    private User user = new User();
    @Override
    public User getModel() {
        return user;
    }
    @Override
    public String execute() throws Exception {
        //获取ActionContext
        ActionContext actionContext = ActionContext.getContext();
        if ("xiao".equals(user.getUsername())
                && "zhbit".equals(user.getPassword())) {
            // 将用户存储在session中.
            actionContext.getSession().put("user", user);
            return SUCCESS;
        } else {
            actionContext.put("msg", "用户名或密码不正确");
            return INPUT;
        }
    }
}

5.main.jsp

在WebContent目录下新建main.jsp;用于用户登录成功后,自动跳转到主页;

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>  
    <title>main.jsp</title>
  </head>
  <body>
    <a href="/LoginPower/operation_add">增加功能</a><br>
    <a href="/LoginPower/operation_update">更新功能</a><br>
    <a href="/LoginPower/operation_del">删除功能</a><br>
    <a href="/LoginPower/operation_find">查询功能</a><br>
  </body>
</html>

6.OperationAction

(用户登录后)用户操作的业务处理;
在项目Java Resource—src的cn.demo.action包中,新建OperationAction.java用于实现用户登录后可执行的操作。

package cn.demo.action;

import com.opensymphony.xwork2.ActionSupport;
@SuppressWarnings("serial")
public class OperationAction extends ActionSupport {
    public String add() {
        System.out.println("增加功能 执行代码……");
        return SUCCESS;
    }
    public String del() {
        System.out.println("删除功能 执行代码……");
        return SUCCESS;
    }
    public String update() {
        System.out.println("更新功能 执行代码……");
        return SUCCESS;
    }
    public String find() {
        System.out.println("查询功能 执行代码……");
        return SUCCESS;
    }
}

7.PrivilegeInterceptor.java

自定义拦截器,用于拦截没登录的用户。
在项目Java Resource—src中新建一个cn.demo.interceptor包,用于存放所有的自定义拦截器。在里面新建PrivilegeInterceptor.java用于实现拦截没登录成功的用户。

package cn.demo.interceptor;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class PrivilegeInterceptor extends AbstractInterceptor {
    private static final long serialVersionUID = 1L;
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        ActionContext actionContext = invocation.getInvocationContext();// 得到ActionContext.
        Object user = actionContext.getSession().get("user");//获取user对象
        if (user != null) {
            return invocation.invoke(); // 继续向下执行.
        } else {
            actionContext.put("msg", "您还未登录,请先登录");
            return Action.LOGIN; //如果用户不存在,返回login值
        }
    }
}

8.success.jsp

在WebContent目录下新建success.jsp,用于用户操作成功所跳转到的成功提示页面;

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>操作成功页面</title>
</head>
<body>
    用户:${user.username} 执行管理操作成功!
</body>
</html>

9.struts.xml

Struts框架的路由配置文件。
在src文件中新建struts.xml配置文件,用于本项目中所有的包、拦截器、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>
    <package name="struts2" namespace="/" extends="struts-default">
        <!-- 声明自定义的拦截器 -->
        <interceptors>
            <!-- 定义:自定义拦截器privilege,用于拦截没登录的用户 -->
            <interceptor name="privilege" 
                         class="cn.demo.interceptor.PrivilegeInterceptor"/>
            <!-- 定义一个拦截器栈myStack,里面包含:默认拦截器以及自定义的 privilege拦截器。-->
            <interceptor-stack name="myStack">
                <interceptor-ref name="defaultStack" />
                <interceptor-ref name="privilege"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        
        <!-- Action:用户登录 -->
        <action name="login" class="cn.demo.action.LoginAction">
            <result>/main.jsp</result>
            <result name="input">/login.jsp</result>
        </action>
        <!-- Action:用户登录后的操作 -->
        <action name="operation_*" class="cn.demo.action.OperationAction"
            method="{1}">
            <result>/success.jsp</result>
            <result name="login">/login.jsp</result>
            <!--在当前Action中,调用上面所定义的拦截器栈myStack -->
            <interceptor-ref name="myStack" />
        </action>
    </package>
</struts>

运行效果

1.项目启动时,默认访问登录页面login.jsp


项目默认启动login.jsp

2.但如果用户直接访问main.jsp时,也能显示相应功能。此时,就需要通过Interceptor拦截器控制用户权限。


main.jsp主页

3.如果用户点击操作功能,则拦截,并返回登录页面
拦截后返回login.jsp登录页

4.只有用户输入用户名:xiao和密码:zhbit后,才被拦截器放行。


登录后可执行操作

作者: 肖sir@ZHBIT
2018 年 09月 27日


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,348评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,122评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,936评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,427评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,467评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,785评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,931评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,696评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,141评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,483评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,625评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,291评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,892评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,492评论 2 348

推荐阅读更多精彩内容