• SpringBoot中Interceptor和Filter的使用


    SpringBoot中Interceptor和Filter的使用

    如何使用拦截器和Filter

    FIlter:过滤器,它是Servlet中的一个概念,主要的作用是对数据进行过滤、校验、记录日志,权限验证等操作。

    使用Filter

    创建类,实现javax.servlet.Filter接口。

    package cn.rayfoo.common.util.FileterController;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * 在SpringBoot中通过注解注册的方式简单的使用Filter
     * @author rayfoo
     */
    @WebFilter(urlPatterns = "/*", filterName = "myfilter")
    public class FileterController implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("Filter初始化中");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
            System.out.println("开始进行过滤处理");
            //调用该方法后,表示过滤器经过原来的url请求处理方法
            filterChain.doFilter(servletRequest, servletResponse);
            System.out.println("处理后的操作");
        }
    
        @Override
        public void destroy() {
            System.out.println("Filter销毁中");
        }
    }
    
    

    上述代码中,重写了Filter的三个方法,分别是:

    • init:在此Filter被创建时执行
    • doFilter:处理Filter的真正业务逻辑,可以在这个方法中对请求进行放行,在放行前后都可以执行代码, 也可以在此方法中进行重定向和请求转发,但是一旦使用了请求转发、重定向,抛出异常,出现异常,被拦截的路径对应的业务方法就不会被执行。
    • destory:在此FIlter被销毁时执行

    SpringBoot中使用Filter

    1、在Filter上加入@WebFilter(urlPatterns = "/path 也可以是*", filterName = "filterName")注解,配置urlPatterns和filterName

    2、在启动类上加入@ServletComponentScan注解

    在SpringBoot中使用Interceptor

    1、创建类,实现HandlerInterceptor接口

    2、Interceptor和Filter有所不同,HandlerInterceptor中有三个方法,由于JDK8之后支持了default关键字,其内的方

    法都是使用default修饰的,不会提示我们手动重写,需要点击进入源码找到并复制到上面创建的类中,修改defalut为public。

    3、在拦截器上加上@Component注解

    package cn.rayfoo.common.interceptor;
    
    import cn.rayfoo.common.exception.MyException;
    import cn.rayfoo.common.response.HttpStatus;
    import cn.rayfoo.common.util.net.ClientUtil;
    import cn.rayfoo.common.util.redis.RedisUtil;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.lang.Nullable;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author rayfoo@qq.com
     * @version 1.0
     * @date 2020/8/6 11:49
     * @description 登录次数校验拦截器
     */
    @NoArgsConstructor
    @Getter
    @Slf4j
    public class AccessInterceptor implements HandlerInterceptor {
    
        @Autowired
        private RedisUtil redisUtil;
    
        /**
         * 需要拦截的URL
         */
        private String InterceptorUrl;
    
        /**
         * 规定的时间范围
         */
        private Long timeBound;
    
        /**
         * 在规定时间按内的访问量
         */
        private Integer requestNum;
    
    
        /**
         * @param InterceptorUrl 要拦截的URL
         * @param timeBound      规定的时间范围
         * @param requestNum     时间范围内的请求次数超过多少时禁止访问
         */
        public AccessInterceptor(String InterceptorUrl, Long timeBound, Integer requestNum) {
            this.InterceptorUrl = InterceptorUrl;
            this.timeBound = timeBound;
            this.requestNum = requestNum;
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //判断该ip地址五分钟内注册了多少次 如果超过五次,将其ip列入黑名单十分钟。 每注册一次给此id加1
            String ipAddr = ClientUtil.getIpAddr(request) + InterceptorUrl;
            //判断ip地址是否存在
            if (redisUtil.hasKey(ipAddr)) {
                //获取五分钟内获取验证码的次数
                Integer count = (Integer) redisUtil.get(ipAddr);
                //如果在x分钟内获取了超过x次
                if (count > requestNum) {
                    //获取过期时间
                    long expire = redisUtil.getExpire(ipAddr);
                    log.error(ClientUtil.getIpAddr(request) + "对" + InterceptorUrl + "操作频繁,此接口已将其暂时列入黑名单");
                    //告知用户限制时间还有多久
                    throw MyException.builder().code(HttpStatus.INTERNAL_SERVER_ERROR.value()).msg("您的操作过于频繁,请" + expire + "秒后重试!").build();
                }
                //不到五次就累加
                redisUtil.incr(ipAddr, 1L);
            } else {
                //不存在的话 创建
                redisUtil.set(ipAddr, 1L);
                //过期时间设置为time秒
                redisUtil.expire(ipAddr, timeBound);
            }
    
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        }
    
    
    }
    
    

    在Interceptor中有三个方法,

        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
    
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception
    
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception 
    

    其中preHandle是在业务方法执行之前执行,返回true表示放行请求,返回false表示不执行拦截的方法, 在此方法中同样可以进行重定向、转发(此时都会返回false),异常抛出等操作,出现此三种情况,都不会执行业务方法。

    postHandle是在业务方法执行之后,但是在视图渲染之前执行,可以进行视图的一些操作,其参数中提供了一个modelAndView可以修改视图的路径和渲染的值。

    afterCompletion是在视图渲染完成后执行的,可以进行关闭资源、日志记录等操作。

    注册Interceptor

    在SpringBoot2中,建议使用的注册拦截器的方法有如下两种:

    • 实现WebMvcConfigurer接口
    • 继承WebMvcConfigurerAdapter类(此类也是实现了WebMvcConfigurer

    下面介绍一下实现WebMvcConfigurer方法注册拦截器

    package cn.rayfoo.common.config;
    
    import cn.rayfoo.common.interceptor.AccessInterceptor;
    import cn.rayfoo.modules.base.interceptor.SMSValidateInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * @author rayfoo@qq.com
     * @version 1.0
     * @date 2020/8/6 9:43
     * @description 拦截器配置类
     */
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        /**
         * 由于使用了其他依赖 将自定义的拦截器作为Bean写入配置
         * @return
         */
        @Bean
        public SMSValidateInterceptor getSMSValidateInterceptor(){
            return new SMSValidateInterceptor();
        }
    
        /**
         * 短信接口的拦截器
         * @return
         */
        @Bean
        public AccessInterceptor getCodeAccessInterceptor(){
            return new AccessInterceptor("/user/code",300L,5);
        }
    
        /**
         * 注册拦截器
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
    
            //注册短信验证码接口的请求次数拦截器
            AccessInterceptor codeAccessInterceptor = getCodeAccessInterceptor();
            registry.addInterceptor(codeAccessInterceptor)
                    .addPathPatterns(codeAccessInterceptor.getInterceptorUrl());
    
            //注册手机号校验拦截器
            registry.addInterceptor(getSMSValidateInterceptor())
                    .addPathPatterns("/user/code");
    
        }
    
    }
    
    

    在此类中,可以使用@Bean来创建多个拦截器对象,使用addInterceptors进行注册。注册时,需要提供拦截的路径、不拦截的路径。均为可选参数。

    执行顺序

    Interceptor和Filter的执行顺序是不同的,下面的图很清晰的描述了他们的执行顺序。

    图2来自博客

    拦截器链的执行顺序

    当拦截器有2个或者两个以上的时候,他们的顺序如何指定,又如何指定拦截器的顺序呢?

    在拦截器注册时,注册的顺序决定着他们执行的顺序。先注册的拦截器会先执行。具体的执行链可以参考下图:

    什么时候使用Filter?什么时候使用Interceptor

    拦截器和Filter都是对AOP思想的一种体现,都可以进行权限校验,日志记录等工作。

    1. 对于没有使用Spring的框架肯定是使用Filter
    2. 对于Spring项目可以根据Filter和Interceptor的执行顺序来灵活使用
    3. 大量的请求块信息处理使用filter, 特别的内部逻辑处理所使用aspect
    4. filter的作用范围中可以包含aspect和interceptor
    5. 在拦截器中可以注入Spring中的Bean对象

    Interceptor中如何使用SpringBean?

    在拦截器中可以直接通过@Autowired注入Spring中的Bean对象,但是一定要注意一点:

    注册拦截器时,必须通过new的形式(包括上面用到的通过方法的返回值new+@Bean注解放入容器)创建拦截器。不能在拦截器上加入@Component注解在WebMvcConfigurer中配置。

    Interceptor可以使用全局异常处理吗?

    当然是可以的,也可以在拦截器中抛出自定义异常交由全局异常处理处理,这样就可以返回和Controller方法相同的返回值类型啦~

  • 相关阅读:
    HttpModule & HttpHandler
    试图运行项目时出错:无法启动调试 没有正确安装调试器
    DataTable 手动排序 无效的问题解决方法??
    合并多个DLL文件
    恢复mysql方法
    在不同版本的IIS下使用ASP.NET MVC
    Enterprise Library 4.1 October 2008
    去除HTML标记
    Mac相关命令
    Mac常用快捷键
  • 原文地址:https://www.cnblogs.com/zhangruifeng/p/13446909.html
Copyright © 2020-2023  润新知