【SpringMVC从入门到实战教程】第八章 SpringMVC 拦截器

八、SpringMVC 拦截器

8.1 什么是拦截器

	在系统中,经常需要在处理用户请求之前和之后执行一些行为,例如检测用户的权限,或者将请求的信息记录到日志中,即平时所说的“权限检测”及“日志记录”。当然不仅仅这些,所以需要一种机制,拦截用户的请求,在请求的前后添加处理逻辑。

    Spring MVC 提供了 Interceptor 拦截器机制,用于请求的预处理和后处理。

    在开发一个网站时可能有这样的需求:某些页面只希望几个特定的用户浏览。对于这样的访问权限控制,应该如何实现呢?拦截器就可以实现上述需求。在 Struts2 框架中,拦截器是其重要的组成部分,Spring MVC 框架也提供了拦截器功能。

    Spring MVC 的拦截器(Interceptor)与 Java Servlet 的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
    
    将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。

拦截器和过滤器的区别:

在这里插入图片描述

组件 过滤器Filter 拦截器 Interceptor
来源 来源于Servlet,重量级的组件 来源于SpringMVC框架,只能用在这个框架中
拦截范围 所有的资源:Servlet,JSP,HTML 只能拦截处理器

8.2 maven依赖

<dependencies>
    <!-- spring核心包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springbean包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springcontext包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- spring表达式包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springAOP包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springAspects包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- spring对web的支持 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springwebMVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>

    <!-- 配置javaweb环境 -->
    <!-- servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!-- jsp-api -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- jstl -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <!-- jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.9.5</version>
    </dependency>
</dependencies>

8.3 SpringMVC配置

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--字符编码过滤器-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 通过隐藏域传参解决form表单的PUT与DELETE方式的请求 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 解决put请求传递参数的问题-->
    <filter>
        <filter-name>HttpPutFormContentFilter</filter-name>
        <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HttpPutFormContentFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>/views/index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

springmvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- springmvc的注解式开发 -->
    <!-- 开启组件扫描 -->
    <context:component-scan base-package="com.newcapec"/>

    <!-- MVC注解驱动 -->
    <mvc:annotation-driven/>

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置视图地址的前缀和后缀:简化视图地址 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--不拦截静态资源-->
    <mvc:default-servlet-handler/>
</beans>

8.4 Controller

DemoController.java:

@Controller
@RequestMapping("/demo")
public class DemoController {
    
    

    @RequestMapping("/find")
    public String find() {
    
    
        System.out.println("DemoController的find方法...");
        return "success";
    }

    @RequestMapping("/add")
    public String add() {
    
    
        System.out.println("DemoController的add方法...");
        return "success";
    }

    @RequestMapping("/edit")
    public String edit() {
    
    
        System.out.println("DemoController的edit方法...");
        return "success";
    }

    @RequestMapping("/remove")
    public String remove() {
    
    
        System.out.println("DemoController的remove方法...");
        return "success";
    }
}

EmpController.java:

@Controller
@RequestMapping("/emp")
public class EmpController {
    
    

    @RequestMapping("/find")
    public String find() {
    
    
        System.out.println("EmpController的find方法...");
        return "success";
    }

    @RequestMapping("/add")
    public String add() {
    
    
        System.out.println("EmpController的add方法...");
        return "success";
    }

    @RequestMapping("/edit")
    public String edit() {
    
    
        System.out.println("EmpController的edit方法...");
        return "success";
    }

    @RequestMapping("/remove")
    public String remove() {
    
    
        System.out.println("EmpController的remove方法...");
        return "success";
    }
}

8.5 拦截器定义

实现HandlerInterceptor接口,并且实现其中三个抽象方法。

/**
 * springmvc中的拦截器对象
 * 1.编写代码:实现拦截器接口 HandlerInterceptor
 * 2.配置拦截器
 */
public class Demo1Interceptor implements HandlerInterceptor {
    
    

    /**
     * preHandle方法,预处理
     * 作用:在请求进入指定Controller方法之前,执行的方法(对请求进行验证)
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param handler  请求将要执行的Controller对象
     * @return 布尔类型:
     *		true表示请求放行,继续向后执行(有可能进入下一个拦截器,也有可能进入Controller)
     * 		false表示拦截请求,请求不能继续向后执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("Demo1Interceptor拦截器中的preHandle方法执行了...");
        return true;
    }

    /**
     * postHandle 后处理
     *
     * @param request      请求对象
     * @param response     响应对象
     * @param handler      正在执行的Controller对象
     * @param modelAndView 模型和视图数据,Controller方法执行完成之后的返回值
     *                     注意:此方法必须在请求进入Controller之后才会执行,如果没有进入Controller是不会执行的
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("Demo1Interceptor拦截器中的postHandle方法执行了...");
    }

    /**
     * afterCompletion 请求处理完成之后
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param handler  已经执行过的Controller对象
     * @param ex       Controller方法抛出的异常对象
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        System.out.println("Demo1Interceptor拦截器中的afterCompletion方法执行了...");
    }
}

方法解析:

	1、preHandle方法
       在请求进入Controller方法之前调用此方法,起到拦截的作用;
       如果preHandle方法返回值为true,表示放行(允许进入Controller方法);  
       如果preHandle方法返回值为false,表示拦截(不允许进入Controller方法);
       
    2、postHandle方法
       请求进入Controller方法之后,但未结束之前,调用此方法;  
       可在返回的模型数据进行处理加工,比如加入公用信息以便页面显示;
       
    3、afterCompletion方法
       请求离开Controller方法之后,调用此方法;
       可获取异常信息,记录日志,资源清理等;

执行流程解析:

	后端控制器方法执行之前,先执行拦截器preHandle(),后端控制器方法执行结束之后,执行的是拦截器的postHandle(),当前端控制器解析控制层返回的ModelandView并交给视图解析器渲染解析完成后,才会执行拦截器的afterCompletion()方法。

在这里插入图片描述

8.6 拦截器配置

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 配置一个拦截器 -->
    <mvc:interceptor>
        <!-- 配置此拦截器所拦截的路径
			例如:/emp/find,仅当前此路径的请求会进入拦截器
				 /emp/*,所有以/emp开头的路径都会进入拦截器(路径层级固定),/emp/find会进入,但是/emp/find/1不会进入
				 /emp/**,所有以/emp开头的路径都会进入拦截器(路径层级不固定),/emp/find会进入,/emp/find/1也会进入
            	 /** 表示所有层级的所有路径都会进入拦截器
			注意:/**所有特指所有进入到SpringMVC流程中的请求,或者进入到前端控制器的请求。
        -->
        <mvc:mapping path="/**"/>
        <!-- 注册拦截器对象 -->
        <bean class="com.newcapec.interceptor.Demo1Interceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

/*/**的区别:

请求路径 解析
/find 配置/*或/**可进入
/demo/find 配置/**可进入

8.7 页面

index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center">
        <h1>拦截器</h1>
        <h2>当前登录用户:${sessionScope.loginUser}</h2>
        <a href="demo/find">测试查询</a><br>
        <a href="demo/add">测试新增</a><br>
        <a href="demo/edit">测试编辑</a><br>
        <a href="demo/remove">测试删除</a><br>
        <a href="emp/find">员工查询</a><br>
        <a href="emp/add">员工新增</a><br>
        <a href="emp/edit">员工编辑</a><br>
        <a href="emp/remove">员工删除</a><br>
        <a href="auth/logout">退出系统</a><br>
    </div>
</body>
</html>

success.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center">
        <h1>操作成功</h1>
    </div>
</body>
</html>

8.8 拦截器执行流程

定义第二个拦截器Demo2Interceptor.java:

public class Demo2Interceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("第二个拦截器中的preHandle方法执行了...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("第二个拦截器中的postHandle方法执行了...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        System.out.println("第二个拦截器中的afterCompletion方法执行了...");
    }
}

配置:

<!-- 多个拦截器的执行顺序,是按照配置的先后顺序 -->
<mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="com.newcapec.interceptor.Demo2Interceptor"/>
</mvc:interceptor>

8.8.1 正常运行流程

每个拦截器的preHandler方法都返回true。

执行结果:

Demo1Interceptor拦截器中的preHandle方法执行了...
第二个拦截器中的preHandle方法执行了...
DemoController的find方法...
第二个拦截器中的postHandle方法执行了...
Demo1Interceptor拦截器中的postHandle方法执行了...
第二个拦截器中的afterCompletion方法执行了...
Demo1Interceptor拦截器中的afterCompletion方法执行了...

8.8.2 中断流程

Demo1Interceptor的preHandler方法返回true,Demo2Interceptor返回false。

执行结果:

Demo1Interceptor拦截器中的preHandle方法执行了...
第二个拦截器中的preHandle方法执行了...
Demo1Interceptor拦截器中的afterCompletion方法执行了...

8.8.3 中断流程

每个拦截器的preHandler方法都返回false。

执行结果:

Demo1Interceptor拦截器中的preHandle方法执行了...

8.8.4 总结

  • preHandle方法按拦截器定义顺序调用,postHandler方法按拦截器定义逆序调用,afterCompletion方法按拦截器定义逆序调用。
  • postHandler方法在拦截器链内所有拦截器返true时调用(处理器方法执行了,才执行)。
  • afterCompletion方法只要preHandle方法执行过,并且返回true时就会调用。

在这里插入图片描述

8.9 拦截器应用

	用户身份认证拦截器的实现。

8.9.1 Controller

@Controller
@RequestMapping("/auth")
public class LoginController {
    
    

    @RequestMapping("/login")
    public String login(String username, String password, HttpSession session, Model model) {
    
    
        //模拟查询:根据用户名和密码查询数据
        if ("admin".equals(username) && "123".equals(password)) {
    
    
            //比对成功,用户登录
            //将用户信息放入session域对象
            session.setAttribute("loginUser", username);
            return "index";
        }
        model.addAttribute("message", "username or password is invalid");
        return "login";
    }

    @RequestMapping("/logout")
    public String logout(HttpSession session) {
    
    
        session.removeAttribute("loginUser");
        session.invalidate();
        return "login";
    }
}

8.9.2 拦截器

public class LoginInterceptor implements HandlerInterceptor {
    
    
    
    /**
     * 身份认证:
     * 过滤器:
     * 1.从session域中获取用户的登录信息
     * 2.判断用户信息是否为空
     * 3.不为空(表示已登录)放行(doFilter方法)
     * 4.为空(表示未登录)
     * 5.判断当前请求是否为登录请求或无需登录可执行请求(身份认证白名单)
     * 6.如果为白名单请求,放行
     * 7.如果不是,拦截,跳转login,信息提示...
     * 
     * 拦截器:
     * 1.从session域中获取用户的登录信息
     * 2.判断用户信息是否为空
     * 3.不为空(表示已登录)放行(返回true)
     * 4.为空(表示未登录),拦截,跳转login,信息提示...
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("身份认证拦截器执行.....");
        HttpSession session = request.getSession();
        Object userInfo = session.getAttribute("loginUser");
        if (userInfo == null) {
    
    
            request.setAttribute("message", "you are not login.");
            request.getRequestDispatcher("/views/login.jsp").forward(request, response);
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
    }
}

8.9.3 配置

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <!-- 配置拦截器不拦截路径,可使用通配符 -->
        <mvc:exclude-mapping path="/auth/login"/>
        <!--<mvc:exclude-mapping path="/demo/**"/>-->
        <bean class="com.newcapec.interceptor.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

8.9.4 登录页面

login.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center">
        <h1>用户登录</h1>
        <div style="color: red;">${message}</div>
        <form action="auth/login" method="post">
            <p>用户名:<input type="text" name="username"></p>
            <p>密&emsp;码:<input type="text" name="password"></p>
            <p><button>登录</button></p>
        </form>
    </div>
</body>
</html>

vc:interceptor>
</mvc:interceptors>


### 8.9.4 登录页面

**login.jsp:**

```jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center">
        <h1>用户登录</h1>
        <div style="color: red;">${message}</div>
        <form action="auth/login" method="post">
            <p>用户名:<input type="text" name="username"></p>
            <p>密&emsp;码:<input type="text" name="password"></p>
            <p><button>登录</button></p>
        </form>
    </div>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/ligonglanyuan/article/details/125328269