Human0722's blog Human0722's blog
首页
  • Spring

    • Spring Framework
    • Spring Boot
    • Spring Cloud
  • CCNA
  • Vue

    • Vue2
日本语
导航
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Human0722

Gravity always win
首页
  • Spring

    • Spring Framework
    • Spring Boot
    • Spring Cloud
  • CCNA
  • Vue

    • Vue2
日本语
导航
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Spring Framework

  • Spring Boot

    • HelloWorld
    • 异常处理
    • 参数校验
    • 配置文件
    • Filter & Interceptor
      • Spring MVC 启动流程分析
      • DefineSprintBootStarter
    • Java 类库

    • 数据库

    • 解决方案

    • Java.Content
    • Spring Boot
    Xueliang
    2022-12-09
    目录

    Filter & Interceptor

    # 过滤器和拦截器区别

    filterandinterceptor

    # 过滤器 Filters

    过滤器是webserver的一部分,不属于SpringFramework。可以使用过滤器来控制请求或者阻断请求。
    Spring Security就是基于过滤器实现了认证和授权。通过配置一个名为 DelegatingFilterProxy的拦截器, Spring Security可以拦截所有出入流量,因为基于拦截器,所以可以工作在SpringMVC 外。

    # 拦截器 HandlerInterceptors

    拦截器是 Spring MVC的一部分,工作在 DispatcherServlet 和 Controllers之间。我们可以在 到达Controller之前、View 渲染前后对请求进行拦截处理。

    # Filter的使用

    通过实现接口 javax.servlet.Filter 定义过滤器。

    @Component
    public class LogFilter implements Filter {
    
        private Logger logger = LoggerFactory.getLogger(LogFilter.class);
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
          throws IOException, ServletException {
            logger.info("Hello from: " + request.getLocalAddr());
            chain.doFilter(request, response);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    通过注解 @Component 将过滤器加入到 Spring 容器中,容器会自动处理。

    # HandlerInterceptor 的使用

    在 SpringBoot 中使用拦截器分为两个步骤,首先定义一个拦截器,然后通过配置类配置拦截器。
    通过实现接口HandlerInterceptor定义拦截器,重写三个方法:

    • preHandle() 在执行 Controller 方法之前调用
    • postHandle() 在执行 Controller的渲染 View 代码方法之前执行
    • afterCompletion() 在执行 Controller 的渲染 View 代码方法之后执行
    @Component
    public class LogInterceptor implements HandlerInterceptor {
    
        private static Logger log =  LoggerFactory.getLogger(LogInterceptor.class);
    
        /**
         * 执行控制器方法前执行
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            log.info("Hello from:" + request.getLocalAddr());
            return true;
        }
    
        /**
         * 执行完控制器方法,但在渲染ModelAndView前执行
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        
            log.info("postHandle");
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        /**
         * 控制器方法执行完执行
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        
            log.info("afterCopletion");
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35

    通过实现 WebMvcConfigure 的类,再将这个类放入到容器中实现对 SpringMVC的配置。

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Autowired
        private LogInterceptor logInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(logInterceptor);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # 验证

    添加一个控制器方法,访问这个方法,查看日志打印情况。

    @RestController
    public class TestController {
    
        @GetMapping("/test")
        public String handleTest() {
            return "ResponseContent";
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    通过 curl http://localhost:9001/test 打印日志:

    17:18:15.452 [http-nio-9001-exec-1] INFO  i.g.human0722.demo.filter.LogFilter - Hello from:0:0:0:0:0:0:0:1
    17:18:15.461 [http-nio-9001-exec-1] INFO  i.g.h.d.interceptor.LogInterceptor - Hello from:0:0:0:0:0:0:0:1
    Inside Controller
    17:18:15.478 [http-nio-9001-exec-1] INFO  i.g.h.d.interceptor.LogInterceptor - postHandle
    17:18:15.478 [http-nio-9001-exec-1] INFO  i.g.h.d.interceptor.LogInterceptor - afterCopletion
    
    1
    2
    3
    4
    5

    可以看到执行顺序是: 自定义的拦截器、过滤器的 preHandle方法、postHandle方法、afterCompletion方法。

    如果控制器返回的是 ModelAndView 相关类型,拦截器会被执行2次。如果只想让执行1次,要在第一次执行preHandle()后对请求设置修改,标记为已经处理过,第二次判断这个标志。

    # 总结

    过滤器适合粒度较大的控制,比如认证直接阻断请求。
    过滤器适合粒度更新的控制,比如针对某些方法鉴权。

    # 应用

    # 过滤器过滤请求

    OncePreRequestFilter 是 Filter 的一个子类, 内部代码保证了一次请求该过滤器只会被执行一次。且自动将 ServletRequest 转换成了 HttpServletRequest。

    @Component
    public class LogFilter extends OncePerRequestFilter {
        private Logger log = LoggerFactory.getLogger(LogFilter.class);
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            log.info("Hello from:" + request.getRemoteAddr());
            String token = request.getHeader("token");
            if (!StringUtils.hasLength(token)) {
                response.sendError(HttpStatus.BAD_REQUEST.value(), "Bad Request");
                // 如果没有 return, 会正常进入后续流程,不是直接返回。
                return;
            }
            filterChain.doFilter(request, response);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    # 拦截器配合注解实现鉴权

    1、 先自定义一个权限注解 @Auth,用来表示访问该方法/类需要的权限标识。

    // Auth.class
    import java.lang.annotation.Documented;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.*;
    
    @Documented
    @Target({METHOD, TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Auth {
        String[] permissions() default { };
    
        String logical() default Logical.AND;
    }
    
    // Logical.class
    public class Logical {
        public static final String OR = "logical_or";
        public static final String AND = "logical_and";
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    2、定义拦截器

    @Component
    public class LogInterceptor implements HandlerInterceptor {
    
        private static Logger log =  LoggerFactory.getLogger(LogInterceptor.class);
    
    
        /**
         * 执行控制器方法前执行
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            boolean flag = true;
            String token = request.getHeader("token");
            log.info("Token from LogInterceptor:" + request.getHeader("token"));
            String[] split = token.split(",");
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Boolean clazzHasPermission = true;
                Auth clazzAuth = handlerMethod.getMethod().getDeclaringClass().getAnnotation(Auth.class);
                if (clazzAuth != null) {
                    String[] clazzPermissions = clazzAuth.permissions();
                    List<String> requiredPermissions = new ArrayList<>(Arrays.asList(clazzPermissions));
                    String clazzLogical = clazzAuth.logical();
                    log.info("Clazz require permission: {}, Mode: {}, Has permissions: {}", requiredPermissions, clazzLogical, split);
                    for (String permission: split) {
                        requiredPermissions.remove(permission);
                    }
                    clazzHasPermission = (Logical.OR.equals(clazzLogical) && requiredPermissions.size() < split.length) || (Logical.AND.equals(clazzLogical) && requiredPermissions.size() == 0);
                }
    
                Boolean methodHasPermission = true;
                if (clazzHasPermission) {
                    Auth methodAuth = handlerMethod.getMethod().getAnnotation(Auth.class);
                    if (methodAuth != null) {
                        String[] methodPermissions = methodAuth.permissions();
                        String methodLogical = methodAuth.logical();
                        List<String> requiredPermissions = new ArrayList<>(Arrays.asList(methodPermissions));
                        log.info("Method require permissions: {}, Mode: {}, Has permissions: {}", requiredPermissions, methodLogical, split);
                        for (String  permission : split) {
                            requiredPermissions.remove(permission);
                        }
                        methodHasPermission = (Logical.OR.equals(methodLogical) && requiredPermissions.size() < split.length) || (Logical.AND.equals(methodLogical) && requiredPermissions.size() == 0);
                    }
    
                }
    
                flag = clazzHasPermission && methodHasPermission;
            }
            if (!flag) {
                response.reset();
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            }
            return flag;
        }
    
        /**
         * 执行完控制器方法,但在渲染ModelAndView前执行
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        /**
         * 控制器方法执行完执行
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71

    3、配置拦截器

    import io.github.human0722.demo.interceptor.LogInterceptor;
    import lombok.RequiredArgsConstructor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    @RequiredArgsConstructor
    public class InterceptorConfig implements WebMvcConfigurer {
    
        final private LogInterceptor logInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(logInterceptor);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    配置文件
    Spring MVC 启动流程分析

    ← 配置文件 Spring MVC 启动流程分析→

    最近更新
    01
    DefineSprintBootStarter
    03-23
    02
    Spring MVC 启动流程分析
    03-23
    03
    Redis
    03-23
    更多文章>
    Theme by Vdoing | Copyright © 2019-2024 Human0722 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式