过滤器(Filter)与拦截器

过滤器(Filter)与拦截器

https://www.yuque.com/bigbeardhk/java/glico7


一、过滤器(Filter)

1.概念

  • 什么是过滤器

过滤器(Filter)是处于客户端与服务器目标资源之间的一道过滤技术。

  • 过滤器作用

在访问目标资源文件之前,通过一系列的过滤器对请求进行修改、判断等,把不符合规则的请求在中途拦截或修改。也可以对响应进行过滤,拦截或修改响应。例如:实现权限访问控制、过滤敏感词汇、压缩响应信息功能、简单登录认证、ip黑名单封禁请求等。

2.过滤的过程

3.代码实现

存在注解与配置类实现两种方法,推荐配置类实现

  • 简单的登录认证过滤
/**
 * Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
 */

package com.hk.study.config;

import com.hk.study.filter.LoginFilter;
import com.hk.study.filter.TestFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * 过滤器配置类
 *
 * @Author : bigbeardhk
 * @Date : 2022/05/03 15:51
 **/
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean authFilterRegistation(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        //注册bean
        registrationBean.setFilter(new LoginFilter());
        //设置bean name
        registrationBean.setName("LoginFilter");
        //设置属性值
        Map<String, String> initParameters = new HashMap<>(2);
        initParameters.put("includeUrls","/hello/login,/swagger-ui.html");
        registrationBean.setInitParameters(initParameters);
        //拦截所有请求
        registrationBean.addUrlPatterns("/*");
        //执行顺序,数字越小优先级越高
        registrationBean.setOrder(1);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean testFilterRegistation(){
        System.out.println("=========过滤器配置Bean方法testFilterRegistation===========");
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        //注册bean
        registrationBean.setFilter(new TestFilter());
        //设置bean name
        registrationBean.setName("TestFilter");
        //拦截所有请求
        registrationBean.addUrlPatterns("/*");
        //执行顺序,数字越小优先级越高
        registrationBean.setOrder(9);
        return registrationBean;
    }
}

/**
 * Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
 */

package com.hk.study.filter;

import com.hk.study.util.StringUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 登录认证
 *
 * @Author : bigbeardhk
 * @Date : 2022/05/03 15:35
 **/
@Slf4j
public class LoginFilter implements Filter {
    /**
     * 不需要登录就可以访问的路径(比如:注册登录等)
     */
    private String includeUrls;

    /**
     * 只在tomcat容器启动时执行一次
     *
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //获取初始化filter的参数
//        this.includeUrls = filterConfig.getInitParameter("includeUrls");
        this.includeUrls = "/hello/login,/swagger-ui.html";
    }

    /**
     * 客服端发生请求时doFilter前置执行,服务端返回请求时doFilter后置执行
     * 模拟登录认证
     * 1.拦截使用/*(但登录地址/hello/login放行)
     * 2.所以到达/hello/login接口后,验证密码,
     * 密码正确就会在服务器的session中进行setAttribute("user", name + pass)操作;
     * 3.然后请求其它地址中,通过cooking中sessionId,我们在服务器中发现它对应的
     * session.getAttribute("user")是存在的,说明他登录过,所以放行这次请求
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpSession session = request.getSession();
        String uri = request.getRequestURI();
        log.info("filter url:"+uri);

        //不需要过滤直接传给下一个过滤器
        if (!StringUtil.isEmpty(includeUrls) && includeUrls.contains(uri)) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            //需要过滤器
            // session中包含user对象,则是登录状态
            if(session!=null&&session.getAttribute("user") != null){
                System.out.println("user:"+session.getAttribute("user"));
                filterChain.doFilter(request, response);
            }else{
                response.setContentType("Application/json;charset=UTF-8");
                response.getWriter().write("您还未登录");
                // 重定向到登录页(需要在static文件夹下建立此html文件)
                // response.sendRedirect(request.getContextPath()+"/user/login.html");
                return;
            }
        }
    }

    @Override
    public void destroy() {
    }
}

二、拦截器(HandlerInterceptor)

执行顺序
image.png
代码实现

/**
 * Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
 */

package com.hk.study.intercept;

import lombok.extern.slf4j.Slf4j;
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 : bigbeardhk
 * @Date : 2022/05/03 19:30
 **/
@Slf4j
public class LogInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("日志拦截器preHandle方法执行");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        log.info("日志拦截器postHandle方法执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        log.info("日志拦截器afterCompletion方法执行");
    }
}
/**
 * Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
 */

package com.hk.study.config;

import com.hk.study.intercept.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 拦截器全局配置
 *
 * @Author : bigbeardhk
 * @Date : 2022/05/03 19:32
 **/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //需要拦截的路径,/**表示拦截所有请求
//        String[] addPathPatterns={"/**"};
        String[] addPathPatterns={"/sdgdfgf"};
        //不需要拦截的路径
        String[] excludePathPatterns={"/boot/login","/boot/exit"};
        //新注册的过滤器
        registry.addInterceptor(new LogInterceptor())
                .addPathPatterns(addPathPatterns)
                .excludePathPatterns(excludePathPatterns);

        /*registry.addInterceptor(new AuthIntercepter())
                .addPathPatterns(addPathPatterns)
                .excludePathPatterns(excludePathPatterns);*/
    }
}

三、切面编程(Aspect)

详见SpringAOP的实现


四、拦截器,过滤器,Aspect的区别

  • 共同点

三者都是AOP思想体现

都可以对HttpServletRequest对象进行处理,日志、权限控制等

  • 区别

Filter属于Servlet规范,Intercepter、Spring AOP属于Spring框架

实现AOP的方式不同,Filter用回调函数实现,一般情况下拿不到Spring bean对象,Intercepter用责任链实现,Spring AOP基于动态代理

  • 执行顺序

image.png

clipboard.png


五、常见问题

  1. 过滤器各方法的执行时机?
  • init()与destroy()方法只会在tomcat(项目)启动或关闭时执行一次(一定会执行,不受配置URL影响),不会在客服端请求后再次执行
  • doFilter()在客服端请求(根据配置判断是否拦截)时执行,request请求时,执行doFilter上面代码;response响应时,执行doFilter下面代码;
  1. 过滤器,拦截器的执行顺序?

image.png


资料学习与引用

posted @ 2022-05-04 10:26  JerryMouseJDK  阅读(1029)  评论(0编辑  收藏  举报