JavaWeb过滤器Filter整理

文章目录

  • 1、Filter介绍
  • 2、Filter接口
  • 3、过滤器Filter的使用
  • 4、过滤器的配置
  • 6、Filter的生命周期
  • 7、Filter工作代码

1、Filter介绍

Filter,过滤器,用于对访问web服务器管理的所有web资源(例如Jsp, Servlet, 静态图片文件或静态html文件)的请求进行拦截并做出处理,从而实现一些特殊的功能,如登录控制,权限管理,过滤敏感词汇

JavaWeb过滤器Filter整理_第1张图片

过滤器可以拆为三部分:

  • 放行之前的代码:对请求进行第一次过滤,然后交给后面的代码
  • 放行:将游览器请求放行,如果还有过滤器,那么就继续交给下一个过滤器
  • 放行后的代码:对返回的Web资源再次进行过滤处理

JavaWeb过滤器Filter整理_第2张图片

通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。

2、Filter接口

Servlet API中提供了一个Filter接口

//注意导入时来源选择正确
import javax.servlet.*;

源码如下:

JavaWeb过滤器Filter整理_第3张图片

3、过滤器Filter的使用

使用Filter,只需实现上面的Filter接口,然后重写里面的方法。

import javax.servlet.*;
import java.io.IOException;
 
public class MyFilter implements Filter {

import javax.servlet.*;
import java.io.IOException;
 
public class MyFilter implements Filter {
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 
    }

	@Override
    public void destroy() {
        
    }
}

关于这三个方法:

  • doFilter(ServletRequest, ServletResponse, FilterChain):这是一个完成过滤行为的方法。这同样是上游过滤器调用的方法。引入的FilterChain对象提供了后续过滤器所要调用的信息。如果该过滤器是过滤器链中的最后一个过滤器,则将请求交给被请求资源。也可以直接给客户端返回响应信息。
  • init(FilterConfig):由Web容器来调用完成过滤器的初始化工作。它保证了在第一次doFilter()调用前由容器调用,通过它的形参filterConfig能获取在web.xml 文件或配置类中指定的初始化参数
  • destroy():由Web容器来调用来释放资源,doFilter()中的所有活动都被该实例终止后,调用该方法

接下来写个示例:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*") //过滤路径,WebFilter("/*")表示对所有请求进行过滤
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始化....."+filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤request........doSomeFilter"); 
        //下面这行代码的作用就是放行
        filterChain.doFilter(servletRequest, servletResponse);  
        System.out.println("过滤response.......doSomeFilter");
    }

    @Override
    public void destroy() {
        System.out.println("销毁了.....destroy");
    }

}


4、过滤器的配置

Filter的配置可以通过两种方式实现:

  • Servlet项目中,可以使用@WebFilter注解来进行Filter的配置
  • SpringBoot项目中,通过 FilterRegistrationBean 类来配置

接下来先演示第二种方式,FilterRegistrationBean类源码:泛型中的T即我们自己定义的那个MyFilter

JavaWeb过滤器Filter整理_第4张图片

先自定义两个Filter:

public class MyFilter1 implements Filter {
	
	...
	
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        
        HttpServletRequest request=(HttpServletRequest)servletRequest;
        System.out.println("自定义过滤器filter1触发,拦截url:"+request.getRequestURI());
        filterChain.doFilter(servletRequest,servletResponse);  //放行
    }
    
	...

}

第二个Filter:

public class MyFilter2 implements Filter {
	...
	
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        
        HttpServletRequest request=(HttpServletRequest)servletRequest;
        System.out.println("自定义过滤器filter2触发,拦截url:"+request.getRequestURI());
        filterChain.doFilter(servletRequest,servletResponse);  //放行
    }

	...

}

创建配置类,返回不同的FilterRegistrationBean实例,来配置上面自定义的过滤器:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

/**
 * @author LLG
 * @date 2023/7/23
 */
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<MyFilter1> filter1FilterRegistration(){

        FilterRegistrationBean<MyFilter1> bean = new FilterRegistrationBean<>(new MyFilter1());
        /*以上是使用了有参构造,也可以先无参构造,再set我自定义的过滤器进去,如下:
        FilterRegistrationBean bean = new FilterRegistrationBean<>();
        bean.setFilter(new MyFilter1());*/
        bean.setName("MyFilter1");  //过滤器名称
        bean.addUrlPatterns("/*");  //过滤所有路径
        bean.addInitParameter("exclusions", "/webjars/*,/doc.html");  //设置init方法的参数
        //bean.setOrder(1);   //优先级,最顶级
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE); //也可以直接set一个框架枚举类中的最高优先级(一个最小的负数)
        return bean;
    }

    @Bean
    public FilterRegistrationBean<MyFilter2> filter2FilterRegistrationBean(){
        FilterRegistrationBean<MyFilter2> bean = new FilterRegistrationBean<>(new MyFilter2());
        bean.setName("MyFilter2");
        bean.addUrlPatterns("/user/*");
        bean.setOrder(Ordered.LOWEST_PRECEDENCE);  //最低优先级
        return bean;
    }
}




测试下效果,先访问localhost:servicePort/

JavaWeb过滤器Filter整理_第5张图片
此时只触发了过滤器1,再访问localhost:servicePort/user/3,可以看到两个过滤器都触发了,触发顺序与我设置的优先级一样:

JavaWeb过滤器Filter整理_第6张图片

bean.addInitParameter("exclusions", "/webjars/*,/doc.html"); 

最后再来补充下上面这个addInitParameter方法的用处:即把参数传递给自定义的Filter的init方法的形参filterConfig,filterConfig.getInitParameter(paramName)拿到参数值后用于后续的逻辑。

@Slf4j
public class MyFilter implements Filter {

    private static final String UNKNOWN = "unknown";
    private static final String URL_EXCLUSIONS = "exclusions";
    private static final String URL_SEPARATOR = ",";
    /**
     * 排除的 uri
     */
    private String[] exclusions;


    @Override
    public void init(FilterConfig filterConfig) {
    	//这样就拿到了配置类中要排除的url,即不处理的url
        exclusions = filterConfig.getInitParameter(URL_EXCLUSIONS).split(URL_SEPARATOR);  
        log.info("MyFilter init ...");
    }


	/**
     * 用于判断uri是不是在要排除的uri中
     */
	private boolean isExclusion(String uri) {
        if (Objects.isNull(uri) || Objects.isNull(exclusions)) {
            return false;
        }

        for (String exclusion : exclusions) {
            if (uri.toLowerCase().matches(exclusion.trim().toLowerCase().replace("*", ".*"))) {
                return true;
            }
        }

        return false;
    }




	@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    	//转型一下,方便调用自己独有的方法
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        String requestURI = httpServletRequest.getRequestURI();
		//如果是要排除的uri,则直接放行
        if (isExclusion(requestURI)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } else {
		//不是排除的uri,则做自己的处理后再放行
		...
		}
	}

6、Filter的生命周期

  • 初始化阶段:web应用程序启动时,web服务器将创建Filter的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作,filter对象只会创建一次,init方法也只会执行一次。通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
  • 拦截和过滤阶段: 只要请求资源的路径和拦截的路径相同,那么过滤器就会对请求进行过滤,这个阶段在服务器运行过程中会一直循环
  • 销毁阶段:当服务器(Tomcat)关闭时,服务器创建的Filter也会随之销毁。destroy方法在Filter的生命周期中仅执行一次。在destroy方法中,可以释放过滤器使用的资源。

关于init方法的形参类型FilterConfig

用户在配置自己的Filter时,可以为Filter配置一些初始化参数,如过滤器名称。当web容器实例化Filter对象,调用其init方法时,会把封装了Filter初始化参数的filterConfig对象传递进来。我们在写自己的Filter时,就可以通过filterConfig对象,就可以获得配置类中的信息。相关方法如下:

FilterConfig接口中的方法:

1String getFilterName():得到filter的名称。
2String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
3Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
4public ServletContext getServletContext():返回Servlet上下文对象的引用。

7、Filter工作代码

备份下工作中用到的一个代码,以后方便copy:

@Slf4j
public class RequestLogFilter implements Filter {

    private static final String UNKNOWN = "unknown";

    private static final String URL_EXCLUSIONS = "exclusions";
    private static final String URL_SEPARATOR = ",";

    /**
     * 排除的 uri
     */
    private String[] exclusions;


    @Override
    public void init(FilterConfig filterConfig) {
        exclusions = filterConfig.getInitParameter(URL_EXCLUSIONS).split(URL_SEPARATOR);
        log.info("RequestLogFilter init ...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        String requestURI = httpServletRequest.getRequestURI();

        if (isExclusion(requestURI)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } else {
            ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest);
            ContentCachingResponseWrapper wrapperResponse = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);

            LocalDateTime start = LocalDateTime.now();
            filterChain.doFilter(wrappedRequest, wrapperResponse);
            LocalDateTime end = LocalDateTime.now();

            String method = wrappedRequest.getMethod();
            String body = getBody(wrappedRequest);
            String ip = this.getRequestIp(httpServletRequest);
            int status = wrapperResponse.getStatus();
            String data = getData(wrapperResponse);
            // 返回页面或者文件时,不打印返回结果(但上面getData(wrapperResponse)方法必须执行,否则无法正常返回结果)
            if (!"application/json".equals(wrapperResponse.getContentType())){
                data = UNKNOWN;
            }
            log.info("Request Info: url: {}, method: {}, ip: {}, request: {}, response: {}, statusCode: {}, startTime: {}, endTime: {}, cost: {}",
                    requestURI,
                    method,
                    ip,
                    body,
                    data,
                    status,
                    start.toString(),
                    end.toString(),
                    Duration.between(start, end).toMillis()
            );
        }
    }


    @Override
    public void destroy() {

    }

    private boolean isExclusion(String uri) {
        if (Objects.isNull(uri) || Objects.isNull(exclusions)) {
            return false;
        }

        for (String exclusion : exclusions) {
            if (uri.toLowerCase().matches(exclusion.trim().toLowerCase().replace("*", ".*"))) {
                return true;
            }
        }

        return false;
    }

    private String getBody(HttpServletRequest request) {
        ContentCachingRequestWrapper requestWrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (requestWrapper != null) {
            return getString(requestWrapper.getContentAsByteArray(), request.getCharacterEncoding());
        }
        return UNKNOWN;
    }

    private String getData(HttpServletResponse response) throws IOException {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (responseWrapper != null) {
            byte[] contentAsByteArray = responseWrapper.getContentAsByteArray();
            responseWrapper.copyBodyToResponse();
            return getString(contentAsByteArray, "UTF-8");
        }
        return UNKNOWN;
    }

    /**
     * 如果流里面是个图片或者文件什么的 就是unknown了
     */
    private String getString(byte[] contentAsByteArray, String characterEncoding) {
        if (contentAsByteArray.length > 0) {
            try {
                return new String(contentAsByteArray, 0, contentAsByteArray.length, characterEncoding);
            } catch (UnsupportedEncodingException ignored) {
            }

        }
        return UNKNOWN;
    }

    private String getRequestIp(HttpServletRequest request) {
        String ip = null;

        String ipAddresses = request.getHeader("X-Forwarded-For");
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ipAddresses = request.getHeader("X-Real-IP");
        }

        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }

}

配置类:

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;


@Configuration
@Slf4j
public class FilterConfiguration {

    @Bean
    public FilterRegistrationBean<MyFilter> requestLogFilterRegistration() {

        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(new MyFilter());
        registration.addUrlPatterns("/*");
        registration.addInitParameter("exclusions", "/webjars/*,/doc.html,/swagger-resources,/swagger-resources/*,/swagger-config/*,/v2/api-docs,/v3/api-docs/*");
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);

        return registration;
    }
}

你可能感兴趣的:(JavaDev,开发语言,Java,Filter)