SpringBoot系列——Filter 过滤器

 前言

  本文记录一下在SpringBoot项目中是如何使用Filter过滤器

 

  代码、测试

  Filter过滤器是servlet包下面的东西,因此我们不需要再额外引包

  方法一

  直接实现Filter接口,并使用@Component注解标注为组件自动注入bean

SpringBoot系列——Filter 过滤器
package cn.huanzi.qch.springbootfilter.filter;

import org.springframework.stereotype.Component;

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

@Component
public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

        System.out.println("TestFilter,"+request.getRequestURI());

        //执行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
SpringBoot系列——Filter 过滤器

  查看日志可以发现,SpringBoot已经帮我们注入了一个filter,拦截路径是/*,拦截所有,如果我们需要进一步拦截具体的则需要我们自己在代码里控制

SpringBoot系列——Filter 过滤器

 

 

  方法二

  实现Filter接口,用@WebFilter注解,指定拦截路径以及一些参数,同时需要在启动类使用@ServletComponentScan扫描带@WebFilter、@WebServlet、@WebListener并将帮我们注入bean

  请看官网介绍:https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners-scanning

SpringBoot系列——Filter 过滤器

 

SpringBoot系列——Filter 过滤器
package cn.huanzi.qch.springbootfilter.filter;

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

//配置拦截路径
@WebFilter(filterName = "testFilter",urlPatterns = {"/test"})
public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

        System.out.println("TestFilter,"+request.getRequestURI());

        //执行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
SpringBoot系列——Filter 过滤器 SpringBoot系列——Filter 过滤器
package cn.huanzi.qch.springbootfilter;

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

//自动扫描与当前类的同包以及子包
@ServletComponentScan
@SpringBootApplication
public class SpringbootFilterApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootFilterApplication.class, args);
    }

}
SpringBoot系列——Filter 过滤器

   查看日志发现,以及帮我们注入了testFilter,拦截路径是/test

SpringBoot系列——Filter 过滤器

  只指定拦截路径,不设置filterName一样可以注入

//配置拦截路径
@WebFilter({"/test"})

SpringBoot系列——Filter 过滤器

 

  方法三

   当然了,我们也可以既使用@Component同时也使用@WebFilter

SpringBoot系列——Filter 过滤器
package cn.huanzi.qch.springbootfilter.filter;

import org.springframework.stereotype.Component;

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


//配置拦截路径
@WebFilter(filterName = "testFilter",urlPatterns = {"/test"})
@Component
public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

        System.out.println("TestFilter,"+request.getRequestURI());

        //执行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
SpringBoot系列——Filter 过滤器 SpringBoot系列——Filter 过滤器
package cn.huanzi.qch.springbootfilter;

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

//自动扫描与当前类的同包以及子包
@ServletComponentScan
@SpringBootApplication
public class SpringbootFilterApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootFilterApplication.class, args);
    }

}
SpringBoot系列——Filter 过滤器

  但是做会注入两个bean,如果你的@WebFilter没有指定filterName或者指定的名称与类名相同,由于注入两个相同名称的bean,程序启动报错,叫我们修改其中一个的名字,或者启用覆盖bean

SpringBoot系列——Filter 过滤器

  这里建议如果你硬要采用第三种方法,最好启用覆盖,因为改名将会注入两个bean,处理逻辑一样但拦截路径不一样,这并不是我们想要的,例如:

SpringBoot系列——Filter 过滤器

  启用覆盖

#启用覆盖同名bean
spring.main.allow-bean-definition-overriding=true

SpringBoot系列——Filter 过滤器

  PS:这里额外说一点,如果我们采用第三种方法,@ServletComponentScan放在TestFilter类上@WebFilter也会被扫描到,不需要放在启动类,第二种方法如果也这样做就不行,估计是受到了@Component注解的影响

//配置拦截路径
@WebFilter(filterName = "testFilter",urlPatterns = {"/test"})
@ServletComponentScan
@Component
public class TestFilter implements Filter

分享大师风采

package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.ClassUtils;
public class CharacterEncodingFilter extends OncePerRequestFilter{
    private static final boolean responseSetCharacterEncodingAvailable = ClassUtils.hasMethod(
        class$javax$servlet$http$HttpServletResponse, "setCharacterEncoding", new Class[] { String.class });
    // 需要设置的编码方式,为了支持可配置,Spring把编码方式设置成了一个变量
    private String encoding;
    // 是否强制使用统一编码,也是为了支持可配置
    private boolean forceEncoding;
    // 构造器,在这里,Spring把forceEncoding的值默认设置成了false
    public CharacterEncodingFilter(){
        this.forceEncoding = false;
    }
    // encoding/forceEncoding的setter方法
    public void setEncoding(String encoding){
        this.encoding = encoding;
    }
    public void setForceEncoding(boolean forceEncoding){
        this.forceEncoding = forceEncoding;
    }
    // Spring通过GenericFilterBean抽象类,对Filter接口进行了整合,
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException{
        if ((this.encoding != null) && (((this.forceEncoding) || (request.getCharacterEncoding() == null)))) {
            request.setCharacterEncoding(this.encoding);
            if ((this.forceEncoding) && (responseSetCharacterEncodingAvailable)) {
                response.setCharacterEncoding(this.encoding);
            }
        }
        filterChain.doFilter(request, response);
    }
}

补充案例

import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.FilterChain;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.filter.CharacterEncodingFilter;
/*
 * InvalidCharacterFilter:过滤request请求中的非法字符,防止脚本攻击
 * InvalidCharacterFilter继承了Spring框架的CharacterEncodingFilter过滤器,当然,我们也可以自己实现这样一个过滤器
 */
@Component
public class InvalidCharacterFilter extends CharacterEncodingFilter{
    // 需要过滤的非法字符
    private static String[] invalidCharacter = new String[]{
        "script","select","insert","document","window","function",
        "delete","update","prompt","alert","create","alter",
        "drop","iframe","link","where","replace","function","onabort",
        "onactivate","onafterprint","onafterupdate","onbeforeactivate",
        "onbeforecopy","onbeforecut","onbeforedeactivateonfocus",
        "onkeydown","onkeypress","onkeyup","onload",
        "expression","applet","layer","ilayeditfocus","onbeforepaste",
        "onbeforeprint","onbeforeunload","onbeforeupdate",
        "onblur","onbounce","oncellchange","oncontextmenu",
        "oncontrolselect","oncopy","oncut","ondataavailable",
        "ondatasetchanged","ondatasetcomplete","ondeactivate",
        "ondrag","ondrop","onerror","onfilterchange","onfinish","onhelp",
        "onlayoutcomplete","onlosecapture","onmouse","ote",
        "onpropertychange","onreadystatechange","onreset","onresize",
        "onresizeend","onresizestart","onrow","onscroll",
        "onselect","onstaronsubmit","onunload","IMgsrc","infarction"
    };
 
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException{   
        String parameterName = null;
        String parameterValue = null;
        // 获取请求的参数
        @SuppressWarnings("unchecked")
        Enumeration<String> allParameter = request.getParameterNames();
        while(allParameter.hasMoreElements()){
            parameterName = allParameter.nextElement();
            parameterValue = request.getParameter(parameterName);
            if(null != parameterValue){
                for(String str : invalidCharacter){
                    if (StringUtils.containsIgnoreCase(parameterValue, str)){
                        request.setAttribute("errorMessage", "非法字符:" + str);
                        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/error.jsp");
                        requestDispatcher.forward(request, response);
                        return;
                    }
                }
            }
        }
        super.doFilterInternal(request, response, filterChain);
    }
}

参考文献

https://www.cnblogs.com/huanzi-qch     https://blog.csdn.net/reggergdsg/article/details/52821502
上一篇:向指定URL发送POST请求的方法


下一篇:Linux iptables的使用(自打版)