前言
在springboot配置過濾實現方案有兩種, 一種是基於serlvet 的註解 @WebFilter 進行配置,一種是使用Springboot提供的 FilterRegistrationBean註冊自定義過濾器。
該篇使用的方案是後者,因爲按照我以前使用的記憶裏,這種方式可以避免一些偶然出現的小問題,如:過濾器沒生效;生效後url匹配不生效等。
正文
在開始敲代碼前,先從上帝視角看看我們這次實踐案例,做了些什麼:
BodyReaderHttpServletRequestWrapper
名字顯然是隨便取的, 但是從字面意義來看,就是關於body內容的讀取。
爲什麼要寫一個這樣的東西?
簡單講講:
@RequestBody 這個註解大家並不陌生,post請求裏,規定參數傳遞使用application/json 流數據傳遞(序列化後的json字符串)。
正因爲這個請求體重的流數據,流數據只能讀取一次。
而我們這次實踐案例中,過濾器讀取一次,接口還需要讀取一次, 如果不整點手法,那麼這個流數據明顯不夠用。
因此, 我們採取了 繼承HttpServletRequestWrapper ,創建 BodyReaderHttpServletRequestWrapper 來
將流數據進行復制存儲起來。當無論第一次第二次需要使用到流數據時 ,都去當前存儲起來的body數據裏去讀取。
上代碼,新建 BodyReaderHttpServletRequestWrapper.java :
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
/**
* @Author : JCccc
* @CreateTime : 2020/3/27
* @Description :
**/
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
/**
* 所有參數的集合
*/
private Map<String, String[]> parameterMap;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
BufferedReader reader = request.getReader();
body = readBytes(reader);
parameterMap = request.getParameterMap();
}
@Override
public BufferedReader getReader() throws IOException {
ServletInputStream inputStream = getInputStream();
if (null == inputStream) {
return null;
}
return new BufferedReader(new InputStreamReader(inputStream));
}
@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<>(parameterMap.keySet());
return vector.elements();
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (body == null) {
return null;
}
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
/**
* 通過BufferedReader和字符編碼集轉換成byte數組
*
* @param br
* @return
* @throws IOException
*/
private byte[] readBytes(BufferedReader br) throws IOException {
String str;
StringBuilder retStr = new StringBuilder();
while ((str = br.readLine()) != null) {
retStr.append(str);
}
if (StringUtils.isNotBlank(retStr.toString())) {
return retStr.toString().getBytes(StandardCharsets.UTF_8);
}
return null;
}
}
接着,自定義 第一個過濾器 , CheckUserFilter.java:
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
/**
* @Author : JCccc
* @CreateTime : 2020/3/27
* @Description :
**/
public class CheckUserFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
System.out.println("過濾器一初始化");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
System.out.println("進入到第一個過濾器,執行相關邏輯處理");
HttpServletRequest request = (HttpServletRequest) req;
BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
// 從Request的包裝類中讀取數據
BufferedReader reader = requestWrapper.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
System.out.println(sb.toString());
filterChain.doFilter(requestWrapper, res);
}
@Override
public void destroy() {
System.out.println("過濾器一銷燬了");
}
}
然後再自定義一個過濾器,CheckUserFilterNext.java :
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
/**
* @Author : JCccc
* @CreateTime : 2020/3/27
* @Description :
**/
public class CheckUserFilterNext implements Filter {
@Override
public void init(FilterConfig filterConfig) {
System.out.println("過濾器二初始化");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
System.out.println("進入到第二個過濾器,執行相關邏輯處理");
HttpServletRequest request = (HttpServletRequest) req;
BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
// 從Request的包裝類中讀取數據
BufferedReader reader = requestWrapper.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
System.out.println(sb.toString());
filterChain.doFilter(requestWrapper, res);
}
@Override
public void destroy() {
System.out.println("過濾器二銷燬了");
}
}
然後是將這兩個過濾器都丟進spring容器裏面去,順便配置一些 攔截的url和執行順序(畢竟是兩個過濾器,肯定有執行順序):
那麼我們來到 application加上相關代碼:
/**
* 第一個過濾器配置
*
*/
@Bean
CheckUserFilter getCheckUserFilter(){
return new CheckUserFilter();
}
@Bean("checkUserFilter")
public FilterRegistrationBean<CheckUserFilter> checkUserFilter(CheckUserFilter checkUserFilter) {
FilterRegistrationBean<CheckUserFilter> registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(checkUserFilter);
registrationBean.addUrlPatterns("/test/*"); //url攔截
registrationBean.setOrder(1);
registrationBean.setAsyncSupported(true);
return registrationBean;
}
/**
* 第二個過濾器配置
*
*/
@Bean
CheckUserFilterNext getCheckUserFilterNext(){
return new CheckUserFilterNext();
}
@Bean("checkUserFilterNext")
public FilterRegistrationBean<CheckUserFilterNext> checkUserFilterNext(CheckUserFilterNext checkUserFilterNext) {
FilterRegistrationBean<CheckUserFilterNext> registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(checkUserFilterNext);
registrationBean.addUrlPatterns("/test/*"); //url攔截
registrationBean.setOrder(2);
registrationBean.setAsyncSupported(true);
return registrationBean;
}
若想要配置第三個過濾器,那麼也是一樣,自定義一個過濾器繼承Filter,然後再一樣註冊到application裏面去。
接下來我們開始寫點接口去測試一下,
新建一個 MyTestController.java :
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @Author : JCccc
* @CreateTime : 2020/3/27
* @Description :
**/
@Controller
@RequestMapping("/test")
public class MyTestController {
@ResponseBody
@RequestMapping(value="testFilter",method={RequestMethod.POST})
public void testFilter(@RequestBody String jsonStr) {
System.out.println("aaaaa");
System.out.println(jsonStr);
}
}
項目跑起來,可以看到:
咱們剛剛配置的過濾器都已經初始化準備好了,
接下來我們調用一下測試接口:
直接看結果:
ok,過濾器的使用就暫且到這吧。