spring boot對json 輸入流資料攔截分析處理
阿新 • • 發佈:2019-01-29
之前突發奇想通過過濾器和攔截器把前端的請求資料攔截下來然後做些驗證和過濾再到contrlller層做業務邏輯,就嘗試著把請求中的json(到了httprequest類裡是 輸入流資料),攔截下來驗證,but有個問題,就是spring boot httprequest 的getInputStream呼叫一次獲取輸入流後就再次呼叫就會獲取為空。so搞了幾天,現在分享下解決方案。
通過重寫HttpServletRequestWrapper以及過濾器,再封裝資料便於攔截器讀取資料進行處理,從而規避了HttpServletRequestWrapper的輸入流只能讀取一次的問題。
一: 配置
spring boot 簡化了原本spring mvc 的xml配置,配置上簡易了很多。下面簡略的闡述下步驟
配置攔截器 @Configuration 表示這個是一個配置類 繼承WebMvcConfigurerAdapter 並重寫addInterceptors 方法就可以實現攔截器配置
@Configuration public class InterceptorConfigurer extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { // 多個攔截器組成一個攔截器鏈 // addPathPatterns 用於新增攔截規則 // excludePathPatterns 使用者排除攔截 // 驗證字元 registry.addInterceptor(new SpecialCharacterInterceptor()).addPathPatterns("/api/**") .excludePathPatterns("/api/sendToOneNormal")// Im傳送 .excludePathPatterns("/api/sendToGroupNormal");// Im傳送 // 驗證sql注入 registry.addInterceptor(new SQLInjectionInterceptor()).addPathPatterns("/api/**") .excludePathPatterns("/api/saveFormAttr")// 表單設計儲存 .excludePathPatterns("/api/sendToOneNormal")// Im傳送 .excludePathPatterns("/api/sendToGroupNormal");// Im傳送 // registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**"); super.addInterceptors(registry); } }
過濾器配置
@Configuration public class FilterConfiguration { /** * 配置過濾器 * @return */ @Bean public FilterRegistrationBean someFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(SealFilter()); registration.addUrlPatterns("/*"); registration.addInitParameter("paramName", "paramValue"); registration.setName("SealFilter"); return registration; } /** * 建立一個bean * @return */ @Bean(name = "SealFilter") public Filter SealFilter() { return new SealFilter(); } }
二 過濾器實現重新封裝 輸入流
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub ServletRequest requestWrapper = null; if(request instanceof HttpServletRequest) { //判斷是否是http請求 requestWrapper = new MAPIHttpServletRequestWrapper((HttpServletRequest) request); //再封裝request } if(requestWrapper == null) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } }
public class MAPIHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body; // 報文
final static int BUFFER_SIZE = 4096;
public MAPIHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = InputStreamTOByte(request.getInputStream()); //讀取輸入流
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body); //再封裝資料
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setReadListener(ReadListener readListener) { //在這裡配置讀取監聽 可以使用這個方法限制讀取次數
// TODO Auto-generated method stub
}
};
}
public static byte[] InputStreamTOByte(InputStream in) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER_SIZE];
int count = -1;
while ((count = in.read(data, 0, BUFFER_SIZE)) != -1)
outStream.write(data, 0, count);
data = null;
return outStream.toByteArray();
}
}
通過重寫HttpServletRequestWrapper以及過濾器,再封裝資料便於攔截器讀取資料進行處理,從而規避了HttpServletRequestWrapper的輸入流只能讀取一次的問題。