参考文章:
SpringBoot项目中,拦截器获取Post方法的请求body
spring boot入门(九) springboot的过滤器filter。最完整、简单易懂、详细的spring boot教程。
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();
}
}