Spring Boot 技术探索

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".

10、Spring Boot的自定义Filter

平台环境:

名称

版本号

Mac OS X

10.14.5

JDK

1.8.0_201

Apache Maven

3.6.0

IntelliJ IDEA

2019.1 (Ultimate Edition)

Spring Boot

2.1.6.RELEASE

 

  在项目中使用Filter可以进行访问权限验证、过滤有XSS威胁的字符、记录访问日志等等。Spring Boot默认在项目中添加了两个Filter:OrderedCharacterEncodingFilter和HiddenHttpMethodFilter。并且也可以自定义Filter。

  自定义Servlet Filters现在有两种方法。推荐使用老方法,因为经过测试发现新方法定义的过滤器的优先级不能正确执行,但有规律,见本文最后补充。

 

一、老方法自定义Filter步骤(推荐):

  • 自定义一个类,并实现javax.servlet.Filter接口
  • 通过FilterRegistrationBean类把自定义Filter添加到Servlet 3.0+容器的过滤链中

 

1、自定义类

package com.example.demo.filter;


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

public class TestFilter implements Filter
{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        // TODO Auto-generated method stub
        Filter.super.init(filterConfig);
        // 在这里可以获取到FilterRegistrationBean里addInitParameter里设置的参数
        System.out.println(filterConfig.getInitParameter("paramName"));
    }

    @Override
    public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain fChain) throws IOException, ServletException
    {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) sRequest;
        // 获取并打印出过滤到的RequestURI
        System.out.println("TestFilter URL:" + request.getRequestURI());
        // 最后要把请求加入到过滤链中
        fChain.doFilter(sRequest, sResponse);
    }
}

 

2、定义一个WebConfiguration类,把Filter加入过滤链中

package com.example.demo.config;

import com.example.demo.filter.TestFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfiguration
{
    @Bean
    public FilterRegistrationBean<TestFilter> testFilterRegistration()
    {
        // 通过FilterRegistrationBean类把自定义Filter添加到Servlet 3.0+容器中
        FilterRegistrationBean<TestFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new TestFilter()); // 设置自定义Filter
        registration.addUrlPatterns("/*"); // 设置匹配规则
        registration.addInitParameter("paramName", "paramValue"); // 在这里给自定义Filter传递初始化参数
        registration.setName("TestFilter"); // 设置注册名称,如果不设置此项,则默认以自定义Filter类的名称做为注册名称
        registration.setOrder(1); // 设置过滤优先级,值越小,越先执行
        return registration;
    }
}

 

3、启动项目,观察控制台:

可以看到,项目在启动过程中,调用了TestFilter的init方法。

 

在浏览器中访问:http://localhost:8080/get/name=JK

可以看到拦截到了访问请求

 

4、新建TestFilter2,并在WebConfiguration注册。以测试执行顺序。

TestFilter2代码:

package com.example.demo.filter;

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

public class TestFilter2 implements Filter
{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        // TODO Auto-generated method stub
        Filter.super.init(filterConfig);
        // 在这里可以获取到FilterRegistrationBean里addInitParameter里设置的参数
        System.out.println(filterConfig.getInitParameter("paramName"));
    }

    @Override
    public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain fChain) throws IOException, ServletException
    {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) sRequest;
        // 获取并打印出过滤到的RequestURI
        System.out.println("TestFilter2 URL:" + request.getRequestURI());
        // 最后要把请求加入到过滤链中
        fChain.doFilter(sRequest, sResponse);
    }
}

 

WebConfiguration代码:

package com.example.demo.config;

import com.example.demo.filter.TestFilter;
import com.example.demo.filter.TestFilter2;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebConfiguration
{
    @Bean
    public FilterRegistrationBean<TestFilter> testFilterRegistration()
    {
        // 通过FilterRegistrationBean类把自定义Filter添加到Servlet 3.0+容器中
        FilterRegistrationBean<TestFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new TestFilter()); // 设置自定义Filter
        registration.addUrlPatterns("/*"); // 设置匹配规则
        registration.addInitParameter("paramName", "paramValue"); // 在这里给自定义Filter传递初始化参数
        registration.setName("TestFilter"); // 设置注册名称,如果不设置此项,则默认以自定义Filter类的名称做为注册名称
        registration.setOrder(1); // 设置过滤优先级,值越小,越先执行
        return registration;
    }
    
    @Bean
    public FilterRegistrationBean<TestFilter2> testFilterRegistration2()
    {
        // 通过FilterRegistrationBean类把自定义Filter添加到Servlet 3.0+容器中
        FilterRegistrationBean<TestFilter2> registration = new FilterRegistrationBean<>();
        registration.setFilter(new TestFilter2()); // 设置自定义Filter
        registration.addUrlPatterns("/*"); // 设置匹配规则
        registration.addInitParameter("paramName", "paramValue2"); // 在这里给自定义Filter传递初始化参数
        registration.setName("TestFilter2"); // 设置注册名称,如果不设置此项,则默认以自定义Filter类的名称做为注册名称
        registration.setOrder(0); // 设置过滤优先级,值越小,越先执行
        return registration;
    }

}

 

在浏览器中访问:http://localhost:8080/get/name=JK

测试发现:

TestFilter2 URL:/get/name=JK
TestFilter URL:/get/name=JK

 

修改TestFilter2,改成setOrder(2),测试发现:

TestFilter URL:/get/name=JK
TestFilter2 URL:/get/name=JK

 

 

二、新方法自定义Filter步骤(不推荐):

1、自定义一个类,并实现javax.servlet.Filter接口

package com.example.demo.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;

import org.springframework.core.annotation.Order;

@Order(1) // 设置过滤优先级,值越小,越先执行(目前测试这个功能无效)
@WebFilter(filterName = "testFilterNew", urlPatterns = {"/*"}, initParams = {@WebInitParam(name = "paramNameNew", value = "paramValueNew")})
// 设置注册名称、匹配规则、初始化参数
public class TestFilterNew implements Filter
{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {
        // TODO Auto-generated method stub
        Filter.super.init(filterConfig);
        System.out.println(filterConfig.getInitParameter("paramNameNew"));
    }

    @Override
    public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain fChain) throws IOException, ServletException
    {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) sRequest;
        // 获取并打印出过滤到的RequestURI
        System.out.println("TestFilterNew URL:" + request.getRequestURI());
        // 最后要把请求加入到过滤链中
        fChain.doFilter(sRequest, sResponse);
    }
}

 

2、在Spring Boot启动类中加入@ServletComponentScan

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan // 扫描Servlet组件
public class DemoApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(DemoApplication.class, args);
    }
}

 

在SpringBootApplication上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。

 

3、启动Spring Boot项目,会看到控制台打印日志中显示:

 

4、在浏览器中输入http://localhost:8080/get/name=JK,可以看到控制台打印出来过滤到的访问信息:

TestFilter URL:/get/name=JK
TestFilter2 URL:/get/name=JK
TestFilterNew URL:/get/name=JK

 

补充:

对于@Order无效的问题

  • 通过FilterRegistrationBean的setOrder方法可以实现顺序。

  • 经过测试发现,自定义Filter类的class类名的字典正序就是@Order注解加载后的实际执行顺序

 

Bootstrap Thumbnail Second
MySQL

MySQL is the world's most popular open source database.

GO

Bootstrap Thumbnail Third
算法基础

本书介绍了什么是计算机算法,如何描述它们,以及如何来评估它们。

GO