Interceptor 拦截器学习:

  • 1、了解spring mvc拦截器的概念、作用!
  • 2、拦截器的实现方式!
  • 3、拦截器执行流程 和 拦截器链!
  • 4、完成登陆拦截案例!

Spring MVC拦截器 ~~简介

Spring mvc的Interceptor拦截器主要用于拦截用户的 url 请求, 并在Controller方法执行前后加入某些特殊处理,例如通过拦截器进行权限验证、记录请求信息的日志、判断用户是否登录等。类似于Filter过滤器。

拦截器的两种实现方式

  • 1、继承 HandlerInterceptorAdapter 抽象类
  • 2、实现 HandlerInterceptor 接口

HandlerInterceptor接口定义了三个方法

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;

    void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
  • preHandle(): 该方法在请求到达Handler(Controller)之前,先执行的前置处理方法。当其返回值为true时,放行request请求, 继续执行拦截器链的下一个拦截器。返回值为false, 则直接中断请求。
  • postHandle(): 该方法在控制器方法调用之后,视图解析之前执行。可以通过该方法对请求域之中的 model(模型数据) 和 view(逻辑视图) 作出进一步的修改。
  • afterCompletion(): 该方法在整个请求完成,即DispatcherServlet 渲染了对应的视图之后执行。该方法一般用于实现一些资源清理、记录日志信息等工作。


【Spring MVC拦截器的执行流程】

spring mvc 禁止目录浏览 springmvc登录拦截_拦截器

1、前端控制器DispatcherServlet接收客户端发送的request请求,

2、DispatcherServlet拿到request请求后, 将其交给Controller进行处理,在这个过程中,如果配置了拦截器,则会先调用拦截器的preHandler()方法进行拦截处理(登陆验证、日志记录. . .)。

【 拦截器链 】

如果配置了多个Interceptor拦截器, 则拦截器呈链式调用(与Filter过滤器链类似)。拦截器的执行顺序与spring mvc配置文件中拦截器的声明顺序有关, 遵从 先声明、先执行 原则。

  • 1、拦截器的preHandler方法按顺序执行, postHandler和afterCompletion方法按逆序执行
  • 2、第一个拦截器的preHandler()方法返回false,则直接中断请求,后面拦截器的preHandler()方法也不会执行了
  • 3、第二个拦截器的preHandler()方法返回false,则直接拦截所有的url请求,后面拦截器的preHandler()方法也不会执行了,但前面preHandler方法返回值为true的拦截器的afterCompletion()方法还会执行。


【spring_mvc.xml各配置节点说明 】

<!--拦截器链-->
<mvc:interceptors>

	<!--拦截器1-->
    <mvc:interceptor>
        <!-- 拦截所有URL请求 -->
        <mvc:mapping path="/**"/>
        <!--拦截所有以/xxController/say结尾的URL请求-->
        <mvc:mapping path="/xxController/say"/>
        <!--不拦截以/xxController/hello结尾的URL请求-->
        <mvc:exclude-mapping path="/xxController/hello"/>
        <!-- 指定自定义拦截器类 -->
        <bean class="com.cd4356.controller.Interceptor1"/>
    </mvc:interceptor>
    
	<!--拦截器2-->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.cd4356.controller.Interceptor2"/>
    </mvc:interceptor>
    
</mvc:interceptors>
  • <mvc:interceptors> 拦截器链
  • <mvc:interceptor> 指定一个过滤器
  • <mvc:mapping>元素用于配置要拦截的请求
  • <mvc:exclude-mapping>元素用来配置不拦截的请求, 一般是在<mvc:mapping>元素使用通配符时, 才使用该元素配合使用
  • <bean> 用来指定拦截器完整的限定类名
  • path 是<mvc:mapping>和<mvc:exclude-mapping>元素的属性, 用来指定请求路径。可以指定具体的路径, 并且 path属性支持通配符, 可以使用通配符指定
  • /XX/xx 拦截所有以/XX/xx结尾的URL请求, 一般XX为Controller类上的映射路径,xx为方法上的映射路径
  • 通配符
  • /* 子目录下的所有URL请求路径, 不包括孙目录
  • /**



登陆验证拦截案例

【User.java】,创建一个User实体类用于保存session信息

package com.cd4356.controller;

public class User {
    private String account;
    private String password;

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

【LoginController.java】

package com.cd4356.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;

@Controller
public class LoginController {

    @RequestMapping("/to_login")
    public String toLogin(){
        return "login";
    }

    @RequestMapping("/login")
    public String login(@RequestParam String account, @RequestParam String password, HttpSession session){
        //判断用户用户是否存在,如果存在则将信息封装到User对象中,然后将User对象保存到session中,不存在则重定向回登陆页面
        if(account.equals("abc") && password.equals("abc")){
            User user = new User();
            user.setAccount(account);
            user.setPassword(password);
            session.setAttribute("user",user);
            return "redirect:/home";
        }
        return "redirect:/to_login";
    }

    @RequestMapping("/home")
    public String home(){
        System.out.println("home");
        return "home";
    }

    @RequestMapping("/hello")
    public String hello(){
        System.out.println("home");
        return "hello";
    }

}

【Interceptor.java】, 自定义登陆验证拦截器, 从session中获取SessionKey对应值, 验证用户是否登陆,已登录则放行, 未登陆则重定向到登陆界面

package com.cd4356.controller;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Interceptor implements HandlerInterceptor {
	 /**
     * 在业务处理器处理请求之前被调用
     * 如果返回false
     *     从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
     * 如果返回true
     *    执行下一个拦截器,直到所有的拦截器都执行完毕
     *    再执行被拦截的Controller
     *    然后再进入拦截器链,从最后一个拦截器往回执行所有的postHandle()
     *    接着再从最后一个拦截器往回执行所有的afterCompletion()
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
		//不拦截URL中含有login的请求,直接放行
        /*String url = httpServletRequest.getRequestURI();
        if (url.toLowerCase().indexOf("login")>=0){
            System.out.println(url);
            return true;
        }*/

		//获取session信息,验证用户是否登陆,已登录则放行,未登陆则重定向到登陆界面
        User user = (User) httpServletRequest.getSession().getAttribute("user");
        if(user!=null){
            return true;
        }
        httpServletResponse.sendRedirect("/to_login");
        return false;
    }

	/**
     * 在业务处理器处理请求执行完成后,生成物理视图之前执行的动作
     * 可在modelAndView中加入数据,比如当前时间
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    }
	
	/**
     * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
     *
     * 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    }
}

【spring_mvc.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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--注解扫描-->
    <context:component-scan base-package="com.cd4356.controller"/>

    <!--拦截器链-->
    <mvc:interceptors>
        <!--登陆验证拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/to_login"/>
            <mvc:exclude-mapping path="/login"/>
            <bean class="com.cd4356.controller.Interceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

【web.xml】, 注册主要servlet

<?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">
	
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring_mvc.xml</param-value>
        </init-param>
        <!--web服务器启动时自动加载-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--拦截所有请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
</web-app>

【login.html】

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
    <style>
        .main{
            width: 300px;
            text-align: center;
            padding: 20px;
            margin-top: 30px;
            margin-left: 30px;
            box-shadow: 0 0 5px 5px #ccc;
        }
    </style>
</head>
<body>
<div class="main">
    <p>登陆</p>
    <form action="/login" method="post">
        <p><input type="text" name="account" placeholder="账号:"></p>
        <p><input type="password" name="password" placeholder="密码: "></p>
        <input type="submit" value="提交">
    </form>
</div>
</body>
</html>

【home.html】

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>home</title>
</head>
<body>
	<h1>欢迎你,来到xxx主页面!</h1>
</body>
</html>

【hello.html】

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>home</title>
</head>
<body>
	<h1>hello,你好!</h1>
</body>
</html>

【运行结果】, 由图可以看出, 在未登录之前, 直接在地址栏中输入具体的URL地址, 请求会被拦截并重定向回登陆页面, 或者登陆账号或密码错误时也会重定向回登陆页面。 在登陆成功后, 就可以通过在地址栏中输入具体的URL地址来访问到具体的页面。

spring mvc 禁止目录浏览 springmvc登录拦截_Interceptor_02

【重点】

Spring MVC拦截器还有个重要关注点, 那就是Spring MVC拦截器不会拦截jsp页面请求, 那我们岂不是可以直接在地址栏中就可以访问到具体的jsp页面啦。