[SpringMVC from entry to actual combat tutorial] Chapter 8 SpringMVC Interceptor

Eight, SpringMVC interceptor

8.1 What is an interceptor

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

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

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

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

The difference between interceptors and filters:

insert image description here

components Filter Filter Interceptor Interceptor
source From Servlet, heavyweight components It comes from the SpringMVC framework and can only be used in this framework
interception range All resources: Servlet, JSP, HTML can only intercept the processor

8.2 maven dependency

<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 Spring MVC configuration

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 Interceptor definition

Implement the HandlerInterceptor interface and implement three of the abstract methods.

/**
 * 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方法执行了...");
    }
}

Method analysis:

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

Execution process analysis:

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

insert image description here

8.6 Interceptor configuration

<!-- 配置拦截器 -->
<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>

/*/**The difference with :

request path analyze
/find Configuration /* or /** can be entered
/demo/find configuration/** accessible

8.7 Pages

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 Interceptor execution process

Define the second interceptor 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方法执行了...");
    }
}

Configuration:

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

8.8.1 Normal operation process

The preHandler method of each interceptor returns true.

Results of the:

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

8.8.2 Interruption process

The preHandler method of Demo1Interceptor returns true, and Demo2Interceptor returns false.

Results of the:

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

8.8.3 Interruption process

The preHandler method of each interceptor returns false.

Results of the:

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

8.8.4 Summary

  • The preHandle method is called in the order defined by the interceptors, the postHandler method is called in the reverse order defined by the interceptors, and the afterCompletion method is called in the reverse order defined by the interceptors.
  • The postHandler method is called when all interceptors in the interceptor chain return true (the processor method is executed before it is executed).
  • The afterCompletion method will be called as long as the preHandle method has been executed and returns true.

insert image description here

8.9 Interceptor Application

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

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 Interceptors

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 Configuration

<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 page

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>

Guess you like

Origin blog.csdn.net/ligonglanyuan/article/details/125328269