Springboot——拦截器

Posted 我爱布朗熊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springboot——拦截器相关的知识,希望对你有一定的参考价值。

目录

一、拦截器概念

二、拦截器的使用

 2.1  拦截器的创建(preHandle实用性最强)

 2.2 将拦截器添加到容器当中

三、拦截器参数

3.1 获取请求头 request.getHeader

3.2 Object handler 是什么参数

         3.3 ModelAndView modelAndView

3.4 Exception ex

3.5 HttpServletRequest request

3.6 HttpServletResponse response

四、多拦截器执行顺序


一、拦截器概念

  拦截器(Interceptor):是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行(类似有保安拦截检查,正常才通过)

  作用:

  • 在指定的方法调用前后执行预先设定的代码(比如权限的校验就是在访问controller之前校验)
  • 阻止原始方法的执行(把原始的方法终止掉)

 

 

拦截器与过滤器的区别

  •  归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
  •  拦截器内容不同:Filter对所有访问进行增强(在Tomcat服务器进行配置),Interceptor仅针对SpringMVC的访问进行增强

二、拦截器的使用

 2.1  拦截器的创建(preHandle实用性最强)

/**
 * 登录检查
 * 1.配置到拦截器要拦截哪些请求
 * 2.把这些配置放在容器中
 *
 * 实现HandlerInterceptor接口
 */
public class LoginInterceptor implements HandlerInterceptor 
    /**
     * 目标方法执行之前
     * 登录检查写在这里,如果没有登录,就不执行目标方法
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
//      获取进过拦截器的路径
        String requestURI = request.getRequestURI();

        //      登录检查逻辑
        HttpSession session = request.getSession();
        Object loginUser = session.getAttribute("loginUser");
        if(loginUser !=null)
//          放行
            return true;
        
//      拦截   就是未登录,自动跳转到登录页面,然后写拦截住的逻辑
        return false;
    

    /**
     * 目标方法执行完成以后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception 
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    

    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception 
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    

 2.2 将拦截器添加到容器当中

@Configuration
//定制SpringMVC的一些功能都使用WebMvcConfigurer
public class AdminWebConfig implements WebMvcConfigurer 

    /**
     * 配置拦截器
     * @param registry 相当于拦截器的注册中心
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) 
//       下面这句代码相当于添加一个拦截器   添加的拦截器就是我们刚刚创建的
         registry.addInterceptor(new LoginInterceptor())
//       addPathPatterns()配置我们要拦截哪些路径 addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
                 .addPathPatterns()
//       excludePathPatterns()表示我们要放行哪些(表示不用经过拦截器)
//       excludePathPatterns("/","/login")表示放行“/”与“/login”请求
//       如果有静态资源的时候可以在这个地方放行
                 .excludePathPatterns("/","/login");
    

三、拦截器参数

3.1 获取请求头 request.getHeader

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
        String contenType = request.getHeader("Content-Type");
        System.out.println("preHandle..."+contenType);
//      放行
        return true;
    

 

 

3.2 Object handler 是什么参数

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
        String contenType = request.getHeader("Content-Type");
        System.out.println(handler);
        System.out.println("preHandle..."+contenType);
//      放行
        return true;
    

使用PostMan发送请求后,控制台出现下面这个样子

class org.springframework.web.method.HandlerMethod

 

这个参数有什么用?

   被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
        String contenType = request.getHeader("Content-Type");
//        System.out.println(handler);
        HandlerMethod hm = (HandlerMethod)handler;
//        通过hm.getMethod()就可以拿到原始执行的对象,拿到这个对象就可以进行反射
         hm.getMethod();
        System.out.println("preHandle..."+contenType);
//      放行
        return true;
    

 

3.3 ModelAndView modelAndView

封装了SpringMVC进行页面跳转的相关数据,但是我们现在都是反JSON,一般不会这么搞了

3.4 Exception ex

通过这个ex可以拿到原始的程序执行过程中出现的异常的

假设controller层抛了异常,在这里是可以拿到异常对象的,但是我们有异常处理机制,所以这里就没有那么大的需求了

如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理

3.5 HttpServletRequest request

   request:请求对象


3.6 HttpServletResponse response

  response:响应对象

四、多拦截器执行顺序

 当配置多个拦截器时,形成拦截器链

 下图是第二个拦截器

 


@Configuration
//定制SpringMVC的一些功能都使用WebMvcConfigurer
public class AdminWebConfig implements WebMvcConfigurer 

    /**
     * 配置拦截器
     * @param registry 相当于拦截器的注册中心
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) 
//       下面这句代码相当于添加一个拦截器   添加的拦截器就是我们刚刚创建的
         registry.addInterceptor(new LoginInterceptor())
//       addPathPatterns()配置我们要拦截哪些路径 addPathPatterns("/**")表示拦截所有请求,包括我们的静态资源
                 .addPathPatterns()
//       excludePathPatterns()表示我们要放行哪些(表示不用经过拦截器)
//       excludePathPatterns("/","/login")表示放行“/”与“/login”请求
//       如果有静态资源的时候可以在这个地方放行
                 .excludePathPatterns("/","/login");

//       第二个拦截器
        registry.addInterceptor(new LoginInterceptor2()).addPathPatterns("/books");
    

那当我们配置了两个拦截器以后,会有一个执行顺序

拦截器链的运行顺序参照拦截器添加顺序为准,下面就是三个拦截器时的执行顺序

 

 

 

SpringBoot.12.SpringBoot中的拦截器

SpringBoot.12.SpringBoot中的拦截器

前言

拦截器(Interceptor)主要是针对特定处理器进行拦截的。也就是说它拦截的对象是我们的Controller请求。我们可以将Controller中的通用代码进行抽取放在拦截器中,就这一点来讲功能跟我们之前讲的AOP类似(Interceptor底层就是利用AOP来实现的)。在实际开发中Interceptor通常用来做权限校验(现在都有成熟的权限校验框架。比如轻量级的Shiro,还有Spring推出的Spring Security

拦截器

介绍

拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似与servlet中的Filter。
SpringMVC 中的 Interceptor 拦截请求是通过 HandlerInterceptor 接口来实现的。

拦截器可以通过以下几种方式进行实现:

  1. 实现 SpringMVC 的 HandlerInterceptor 接口;

  2. 继承实现了 HandlerInterceptor 接口的类,比如 SpringMVC 已经提供的实现了HandlerInterceptor 接口的抽象类 HandlerInterceptorAdapter;

  3. 实现 SpringMVC 的 WebRequestInterceptor 接口;

  4. 继承实现了 WebRequestInterceptor 的类;

具体实现

1.新建项目

在上一节SpringBoot.11.IDEA中如何快速复制当前父项目中的一个Module为新的项目中我们已经准备好了Module – springboot-08-interceptor。如下图所示:

2.MyInterceptor01.java

package com.christy.config.interceptors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/**
 * 自定义拦截器
 * @Author Christy
 * @Date 2021/9/28 10:20
 **/
public class MyInterceptor01 implements HandlerInterceptor 

    private static final Logger log = LoggerFactory.getLogger(MyInterceptor01.class);

    /**
     * 
     * @author Christy
     * @date 2021/9/28 10:22
     * @param request
     * @param response
     * @param handler 当前请求请求的控制器方法对象  InterceptorController#sayHello
     * @return boolean
     *          true:表示放行当前请求
     *          false:终端当前请求 例如做权限校验时如果校验不通过我们可以使用response.sendRedirect(request.getContextPath()+"/login.suffix");跳转到登录界面
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
        log.info("==========MyInterceptor01==========handler=========" + handler);
        log.info("==========MyInterceptor01==========preHandle=========");
        return true;
    

    /**
     *
     * @author Christy
     * @date 2021/9/28 10:28
     * @param request
     * @param response
     * @param handler 当前请求请求的控制器方法对象  InterceptorController#sayHello
     * @param modelAndView 模型和视图  当前请求访问方法的 modelandview 对象
     * @return void
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception 
        log.info("==========MyInterceptor01==========postHandle=========");
    

    /**
     *
     * @author Christy
     * @date 2021/9/28 10:29
     * @param request
     * @param response
     * @param handler 当前请求请求的控制器方法对象  InterceptorController#sayHello
     * @param ex      如果控制器出现异常时异常对象
     * @return void
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception 
        log.info("==========MyInterceptor01==========ex=========", ex);
        log.info("==========MyInterceptor01==========afterCompletion=========");
    

3.配置拦截器

我们进入到MyWebMvcConfigurer.java中,按快捷键Alt+Insert,选择Implement Methods…或者直接按快捷键Alt+Shift+P。如下图所示:

Select Methods to Implement中选择addIntefceptors()这个方法。如下图所示:

修改MyWebMvcConfigurer.java内容如下所示:

import com.christy.config.interceptors.MyInterceptor01;
import com.christy.constants.FileConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author Christy
 * @Date 2021/9/15 16:24
 **/
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer 

    /**
     * 配置拦截器
     * addInterceptor(): 指定拦截器
     * addPathPatterns("/**"):拦截所有Controller请求
     * excludePathPatterns("/file/**"):排除文件操作的请求路径
     * order():指定拦截器执行顺序 int 类型数字: 默认按照自然排序执行 数字相同时,按照配置顺序执行
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) 
        registry.addInterceptor(new MyInterceptor01())
                .addPathPatterns("/interceptor/**")
                .excludePathPatterns("/file/**")
                .order(1);
    

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) 
        /**
         * 文件上传到服务器后并不能在项目中直接访问,需要将磁盘路径映射成虚拟路径使用http://domain/的方式才可以
         */
        String os = System.getProperty("os.name");
        if(os.toLowerCase().startsWith("win"))
            // Windows虚拟路径映射本地磁盘
            registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileWindowsPath);
         else 
            // Linux虚拟路径映射本地磁盘
            registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileLinuxPath);
        
    

4.InterceptorController.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author Christy
 * @Date 2021/9/28 10:44
 **/
@RestController
@RequestMapping("/interceptor")
public class InterceptorController 

    private static final Logger log = LoggerFactory.getLogger(InterceptorController.class);

    @RequestMapping("sayHello")
    public String sayHello()
        log.info("Hello Interceptor…");
        return "interceptor";
    

5.测试

启动项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察页面和控制台输出:

我们改造一下sayHello()方法抛出一个异常,如下所示:

@RequestMapping("sayHello")
public String sayHello()
  log.info("Hello Interceptor…");
  int i = 1/0;
  return "interceptor";

重新启动项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察页面和控制台输出:

多个拦截器的执行顺序

1.MyInterceptor01.java

我们修改一下MyInterceptor01.java如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/**
 * 自定义拦截器
 * @Author Christy
 * @Date 2021/9/28 10:20
 **/
public class MyInterceptor01 implements HandlerInterceptor 

    private static final Logger log = LoggerFactory.getLogger(MyInterceptor01.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
        log.info("==========MyInterceptor01==========1=========");
        return true;
    

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception 
        log.info("==========MyInterceptor01==========2=========");
    

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception 
        log.info("==========MyInterceptor01==========3=========");
    

2.MyInterceptor02.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/**
 * 自定义拦截器
 * @Author Christy
 * @Date 2021/9/28 10:20
 **/
public class MyInterceptor02 implements HandlerInterceptor 

    private static final Logger log = LoggerFactory.getLogger(MyInterceptor02.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
        log.info("==========MyInterceptor02==========4=========");
        return true;
    

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception 
        log.info("==========MyInterceptor02==========5=========");
    

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception 
        log.info("==========MyInterceptor02==========6=========");
    

3.MyWebMvcConfigurer.java

import com.christy.config.interceptors.MyInterceptor01;
import com.christy.config.interceptors.MyInterceptor02;
import com.christy.constants.FileConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author Christy
 * @Date 2021/9/15 16:24
 **/
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer 

    /**
     * 配置拦截器
     * addInterceptor(): 指定拦截器
     * addPathPatterns("/**"):拦截所有Controller请求
     * excludePathPatterns("/file/**"):排除文件操作的请求路径
     * order():指定拦截器执行顺序 int 类型数字: 默认按照自然排序执行 数字相同时,按照配置顺序执行
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) 
        registry.addInterceptor(new MyInterceptor01())
                .addPathPatterns("/interceptor/**")
                .excludePathPatterns("/file/**")
                .order(1);

        registry.addInterceptor(new MyInterceptor02())
                .addPathPatterns("/interceptor/**")
                .excludePathPatterns("/file/**")
                .order(2);
    

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) 
        /**
         * 文件上传到服务器后并不能在项目中直接访问,需要将磁盘路径映射成虚拟路径使用http://domain/的方式才可以
         */
        String os = System.getProperty("os.name");
        if(os.toLowerCase().startsWith("win"))
            // Windows虚拟路径映射本地磁盘
            registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileWindowsPath);
         else 
            // Linux虚拟路径映射本地磁盘
            registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileLinuxPath);
        
    

4.测试

重新启动项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察控制台输出:

多个拦截器注册以后按照注册的顺序以式结构存储,上面我们配置了两个拦截器,MyInterceptor01先入栈,MyInterceptor02后入栈;所以当请求来了之后:

首先,进入MyInterceptor01preHandle打印输出1,再进入MyInterceptor02preHandle打印输出4;这是一个入栈的过程

其次访问方法主体打印输出方法体内的logger

然后,进入MyInterceptor02postHandle打印输出5,再进入MyInterceptor01postHandle打印输出2;这是一个出栈的过程

最后,进入MyInterceptor02afterCompletion打印输出6,再进入MyInterceptor01afterCompletion打印输出3;这也是一个出栈的过程

按照上面的描述如果我们更换两个拦截器的顺序,让MyInterceptor02先入栈,MyInterceptor01后入栈;那么打印的结果应该是4->1->2->5->3->6

我们修改MyWebMvcConfigurer.java的代码更换两个拦截器的顺序如下:

import com.christy.config.interceptors.MyInterceptor01;
import com.christy.config.interceptors.MyInterceptor02;
import com.christy.constants.FileConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author Christy
 * @Date 2021/9/15 16:24
 **/
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer 

    /**
     * 配置拦截器
     * addInterceptor(): 指定拦截器
     * addPathPatterns("/**"):拦截所有Controller请求
     * excludePathPatterns("/file/**"):排除文件操作的请求路径
     * order():指定拦截器执行顺序 int 类型数字: 默认按照自然排序执行 数字相同时,按照配置顺序执行
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) 
        registry.addInterceptor(new MyInterceptor01())
                .addPathPatterns("/interceptor/**")
                .excludePathPatterns("/file/**")
                .order(2);

        registry.addInterceptor(new MyInterceptor02())
                .addPathPatterns("/interceptor/**")
                .excludePathPatterns("/file/**")
                .order(1);
    

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) 
        /**
         * 文件上传到服务器后并不能在项目中直接访问,需要将磁盘路径映射成虚拟路径使用http://domain/的方式才可以
         */
        String os = System.getProperty("os.name");
        if(os.toLowerCase().startsWith("win"))
            // Windows虚拟路径映射本地磁盘
            registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileWindowsPath);
         else 
            // Linux虚拟路径映射本地磁盘
            registry.addResourceHandler(FileConstants.fileVirtualPath + "**").addResourceLocations("file:" + FileConstants.fileLinuxPath);
        
    

然后重启项目,浏览器访问http://localhost:8808/interceptor/sayHello,注意观察控制台输出:

以上是关于Springboot——拦截器的主要内容,如果未能解决你的问题,请参考以下文章

java 登录拦截器

SpringBoot.12.SpringBoot中的拦截器

SpringBoot.12.SpringBoot中的拦截器

SpringBoot.12.SpringBoot中的拦截器

SpringBoot.12.SpringBoot中的拦截器

SpringBoot.12.SpringBoot中的拦截器