SpringBoot 集成过滤器(filter)

本文深入探讨了SpringBoot中过滤器的实现方式,包括注解和bean注入两种方法,同时提供了跨域、请求体获取及非法字符串过滤的具体示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文章:

SpringBoot项目中,拦截器获取Post方法的请求body

spring boot入门(九) springboot的过滤器filter。最完整、简单易懂、详细的spring boot教程。

Spring Boot filter链

关于springboot中添加Filter的方法

springboot-整合filter的两种方式

SpringBoot集成之过滤器

springboot 使用filter过滤器对项目中的请求参数字符串类型前后空格进行去除

SpringBoot脏话过滤、登陆拦截?一看就会!

springboot通过Filter开启跨域

SpringBoot过滤器过滤get及post请求中的XSS和SQL注入


springboot下过滤器有两种实现方式

1.注解方式

使用该过滤器的时候需添加@WebFilter注解,另外还需要@Component注解,将该类作为组件,注入spring容器中。

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.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * Created by linjiaming
 */
@Slf4j
@Component
@WebFilter(value = "/hello")
// @WebFilter(urlPatterns = "/**", filterName = "ParamsFilter", dispatcherTypes = DispatcherType.REQUEST)
// @WebFilter(urlPatterns = { "需要过滤的url" }, filterName = "过滤器的名称")
public class HelloFilter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, 
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        //在DispatcherServlet之前执行  
        System.out.println("############TestFilter1 doFilterInternal executed############");  
        filterChain.doFilter(request, response);  
        //在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后  
        System.out.println("############TestFilter1 doFilter after############");
    }
 
    @Override
    public void destroy() {
    }
}

 值得一提的是,该过滤器的日志打印方式是lombok插件自带的注解@Slf4j。该插件的功能很强大,可自动为实体类生成get,set方法等。使用时需先下载对应插件,以及在pom文件里引入对应的依赖

2.bean注入方式

首先自定义filter,然后在springboot启动类下配置一个过滤的bean,若不在springboot的启动类下配置,在其他的类上使用时,需在类上写上@Configuration,标志这个类是一个配置类。springboot启动类注解@springbootApplication内部含有该注解,所以无需配置。

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 lombok.extern.slf4j.Slf4j;

@Slf4j
public class HelloFilter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, 
                          ServletResponse servletResponse, 
                          FilterChain filterChain) throws IOException, ServletException {
        log.info("进入到过滤器1啦");
        //放行至下一个过滤器
        filterChain.doFilter(servletRequest,servletResponse);
    }
 
    @Override
    public void destroy() {
    }
}
package com.example.demo;
 
import com.example.demo.filter.HelloFilter1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
 
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
 
    @Bean
    public FilterRegistrationBean registrationBean(){
        FilterRegistrationBean filter = new FilterRegistrationBean(new HelloFilter1());
        // filter.addUrlPatterns("/*");       
        filter.addUrlPatterns("/hello");
        
        //多个过滤器时执行顺序
        //filter.setOrder(1);
        return filter;
    }
}

3. 其它示例

3.1 Filter开启跨域

@Slf4j
@Component
@WebFilter(urlPatterns = { "/api/*" }, filterName = "headerFilter")
public class HeaderFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) resp;
        //解决跨域访问报错
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        //设置过期时间
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, client_id, uuid, Authorization");
        // 支持HTTP 1.1.
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        // 支持HTTP 1.0. response.setHeader("Expires", "0");
        response.setHeader("Pragma", "no-cache");
        // 编码
        response.setCharacterEncoding("UTF-8");
        chain.doFilter(request, resp);
    }

    @Override
    public void init(FilterConfig filterConfig) {
        log.info("跨域过滤器启动");
    }

    @Override
    public void destroy() {
        log.info("跨域过滤器销毁");
    }
}

3.2 获取Post方法的请求body

package com.example.wrapperdemo.controller.wrapper;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;

/**
 * @author 01
 * @program wrapper-demo
 * @description 包装HttpServletRequest,目的是让其输入流可重复读
 * @create 2018-12-24 20:48
 * @since 1.0
 **/
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
    /**
     * 存储body数据的容器
     */
    private final byte[] body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);

        // 将body数据存储起来
        String bodyStr = getBodyString(request);
        body = bodyStr.getBytes(Charset.defaultCharset());
    }

    /**
     * 获取请求Body
     *
     * @param request request
     * @return String
     */
    public String getBodyString(final ServletRequest request) {
        try {
            return inputStream2String(request.getInputStream());
        } catch (IOException e) {
            log.error("", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取请求Body
     *
     * @return String
     */
    public String getBodyString() {
        final InputStream inputStream = new ByteArrayInputStream(body);

        return inputStream2String(inputStream);
    }

    /**
     * 将inputStream里的数据读取出来并转换成字符串
     *
     * @param inputStream inputStream
     * @return String
     */
    private String inputStream2String(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("", e);
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("", e);
                }
            }
        }

        return sb.toString();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}
package com.example.wrapperdemo.controller.filter;

import com.example.wrapperdemo.controller.wrapper.RequestWrapper;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @author 01
 * @program wrapper-demo
 * @description 替换HttpServletRequest
 * @create 2018-12-24 21:04
 * @since 1.0
 **/
@Slf4j
     class ReplaceStreamFilter implements Filter {
    @Override
    void init(FilterConfig filterConfig) throws ServletException {
        log.info("StreamFilter初始化...");
    }

    @Override
    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
        chain.doFilter(requestWrapper, response);
    }

    @Override
    void destroy() {
        log.info("StreamFilter销毁...");
    }
}

3.3 非法字符串过滤

RequestWrapper.java

package org.fiend.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;

/**
 * @author langpf 2020/1/2
 * @description 包装HttpServletRequest,目的是让其输入流可重复读
 */
public class RequestWrapper extends HttpServletRequestWrapper {
    private final Logger log = LoggerFactory.getLogger(getClass());

    /**
     * 存储body数据的容器
     */
    private final byte[] body;

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     * @throws IllegalArgumentException if the request is null
     */
    public RequestWrapper(HttpServletRequest request) {
        super(request);

        // 将body数据存储起来
        String bodyStr = getBodyString(request);

        // 非法字符串过滤
        bodyStr = bodyStr.replaceAll("[()#!|;&$><`\\\\\\t\\n\\r\\f\\u0000]", "").replace("../", "");

        body = bodyStr.getBytes(Charset.defaultCharset());
    }

    /**
     * 获取请求Body
     *
     * @param request request
     * @return String
     */
    public String getBodyString(final ServletRequest request) {
        try {
            return inputStream2String(request.getInputStream());
        } catch (IOException e) {
            log.error("", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取请求Body
     * @return String
     */
    public String getBodyString() {
        final InputStream inputStream = new ByteArrayInputStream(body);

        return inputStream2String(inputStream);
    }

    /**
     * 将inputStream里的数据读取出来并转换成字符串
     *
     * @param inputStream inputStream
     * @return String
     */
    private String inputStream2String(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("", e);
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("", e);
                }
            }
        }

        return sb.toString();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

SecurityFilter.java

package org.fiend.filter;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;

/**
 * @author langpf 2020/1/2
 */
public class SecurityFilter implements Filter {
    private final Logger log = LoggerFactory.getLogger(getClass());

    @Override
    public void init(FilterConfig filterConfig) {
        log.info("StreamFilter初始化...");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest hRequest = (HttpServletRequest) request;

        Cookie[] cookies = hRequest.getCookies();
        if (iterateVdCookiesFail(cookies)) {
            return;
        }

        Enumeration<String> headerParams = hRequest.getHeaderNames();
        if (iterateVdElementsFail(hRequest, headerParams, "Header")) {
            return;
        }

        Enumeration<String> attrParams = request.getAttributeNames();
        if (iterateVdElementsFail(hRequest, attrParams, "Attribute")) {
            return;
        }

        Map<String, String[]> map = request.getParameterMap();
        if (vdParameterMapFail(map)) {
            return;
        }

        ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
        chain.doFilter(requestWrapper, response);
    }

    private boolean iterateVdCookiesFail(Cookie[] cookies) {
        if ((null == cookies) || (cookies.length < 1)) {
            return false;
        }

        String comment;
        String name;
        String value;
        for (Cookie cookie : cookies) {
            comment = cookie.getComment();
            if (vdStrFail(comment)) {
                return true;
            }

            name = cookie.getName();
            if (vdStrFail(name)) {
                return true;
            }

            value = cookie.getValue();
            if (vdStrFail(value)) {
                return true;
            }
        }

        return false;
    }

    public boolean vdStrFail(String str) {
        String msg = "Cookie请求错误, 请求参数含有非法字符!";
        if (StringUtils.isNotBlank(str) && (isContainIllegalCharacter(str) || (str.length() > 100))) {
            log.warn(msg + ": " + str);
            return true;
        }

        return false;
    }

    private boolean iterateVdElementsFail(HttpServletRequest request, Enumeration<String> keyElement, String type) {
        String msg = type + "请求错误, 请求参数含有非法字符!";
        String key;
        String[] values;
        while (keyElement.hasMoreElements()) {
            key = keyElement.nextElement();
            if (isContainIllegalCharacter(key)) {
                log.warn(msg + ": " + key);
                return true;
            }

            values = request.getParameterValues(key);
            if (values == null) {
                continue;
            }

            for (String value : values) {
                if (StringUtils.isNotBlank(value) && isContainIllegalCharacter(value)) {
                    log.warn(msg + ": " + value);
                    return true;
                }
            }
        }

        return false;
    }

    private boolean vdParameterMapFail(Map<String, String[]> map) {
        if ((null == map) || (map.size() < 1)) {
            return false;
        }

        String msg = "Parameter 请求错误, 请求参数含有非法字符!";
        String key;
        String[] values;
        for (Map.Entry<String, String[]> entry : map.entrySet()) {
            key = entry.getKey();
            if (isContainIllegalCharacter(key)) {
                log.warn(msg + ": " + key);
                return true;
            }

            values = entry.getValue();
            if (values == null) {
                continue;
            }

            for (String value : values) {
                if (StringUtils.isNotBlank(value) && isContainIllegalCharacter(value)) {
                    log.warn(msg + ": " + value);
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 只允许[0-9][a-z][A-Z]_-[汉字]
     * java判断字符串是否为数字或中文, '_', '.', 或字母  // '-', ' '
     *
     * 各种字符的unicode编码的范围:
     *  汉字:[0x4e00,0x9fa5](或十进制[19968,40869])
     *  数字:[0x30,0x39](或十进制[48, 57])
     *  小写字母:[0x61,0x7a](或十进制[97, 122])
     *  大写字母:[0x41,0x5a](或十进制[65, 90])
     */
    private boolean isContainIllegalCharacter(String str) {
        String regex = "^[-.(),_0-9a-zA-Z\u4e00-\u9fa5]+$";
        return !str.matches(regex);
    }

    @Override
    public void destroy() {
        log.info("StreamFilter销毁...");
    }
}

SecurityFilterConfig.java

package org.fiend.filter;

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

import javax.servlet.Filter;

/**
 * @author langpf 2020/1/2
 */
@Configuration
public class SecurityFilterConfig {
    /**
     * 注册过滤器
     * @return FilterRegistrationBean
     */
    @Bean
    public FilterRegistrationBean<Filter> someFilterRegistration() {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(securityStreamFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("streamFilter");

        return registrationBean;
    }

    /**
     * 实例化StreamFilter
     * @return Filter
     */
    @Bean(name = "securityStreamFilter")
    public Filter securityStreamFilter() {
        return new SecurityFilter();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风情客家__

原创不易,觉得好的话给个打赏哈

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值