SpringBoot项目中拦截器获取Body参数的问题
文章来源:冰巧 时间:2025-03-11
起首尔们要晓得1个题目:
HttpServletRequest的输出淌只可读与1次,假若您正在阻挡器读与了Body的参数,那末正在Controller再次读与时,会曲交报错,缘故以下:
尔们先去瞅观为何HttpServletRequest的输出淌只可读1次,当尔们挪用getInputStream()办法获得输出淌时获得的是1个InputStream对于象,而本质典型是ServletInputStream,它担当于InputStream。
InputStream的read()办法里面有1个postion,标记以后淌被读与到的地位,每读与1次,该标记便会挪动1次,假使读到末了,read()会前往-1,暗示仍旧读与休矣。要是念要从头读与则须要挪用reset()办法,position便会挪动到前次挪用mark的地位,mark默许是0,因此便能重新再读了。移用reset()办法的条件是曾经誊写了reset()办法,固然可否reset也是有前提的,它与绝于markSupported()办法能否前往true。
InputStream默许虚假现reset(),而且markSupported()默许也是前往false。
/***Repositionsthisstreamtothepositionatthetimethe*<code>mark</code>methodwaslastcalledonthisinputstream.**<p>Thegeneralcontractof<code>reset</code>is:**<ul>*<li>Ifthemethod<code>markSupported</code>returns*<code>true</code>,then:**<ul><li>Ifthemethod<code>mark</code>hasnotbeencalledsince*thestreamwascreated,orthenumberofbytesreadfromthestream*since<code>mark</code>waslastcalledislargerthantheargument*to<code>mark</code>atthatlastcall,thenan*<code>IOException</code>mightbethrown.**<li>Ifsuchan<code>IOException</code>isnotthrown,thenthe*streamisresettoastatesuchthatallthebytesreadsincethe*mostrecentcallto<code>mark</code>(orsincethestartofthe*file,if<code>mark</code>hasnotbeencalled)willberesupplied*tosubsequentcallersofthe<code>read</code>method,followedby*anybytesthatotherwisewouldhavebeenthenextinputdataasof*thetimeofthecallto<code>reset</code>.</ul>**<li>Ifthemethod<code>markSupported</code>returns*<code>false</code>,then:**<ul><li>Thecallto<code>reset</code>maythrowan*<code>IOException</code>.**<li>Ifan<code>IOException</code>isnotthrown,thenthestream*isresettoafixedstatethatdependsontheparticulartypeofthe*inputstreamandhowitwascreated.Thebytesthatwillbesupplied*tosubsequentcallersofthe<code>read</code>methoddependonthe*particulartypeoftheinputstream.</ul></ul>**<p>Themethod<code>reset</code>forclass<code>InputStream</code>*doesnothingexceptthrowan<code>IOException</code>.**@exceptionIOExceptionifthisstreamhasnotbeenmarkedorifthe*markhasbeeninvalidated.*@seejava.io.InputStream#mark(int)*@seejava.io.IOException*/publicsynchronizedvoidreset()throwsIOException{thrownewIOException("mark/resetnotsupported");}尔们再去瞅观ServletInputStream,能够观到该类不誊写mark(),reset()和markSupported()办法:
综上,InputStream默许虚假现reset的相干办法,而ServletInputStream也不誊写reset的相干办法,如许便没法反复读与淌,那便是尔们从request对于象中获得的输出淌便只可读与1次的缘故。
处理意图,应用HttpServletRequestWrapper + Filter处理输出淌没有能反复读与题目。
既然ServletInputStream没有帮助从头读写,那末为何没有把淌读出去后用容器保存起去,前面便能够屡次哄骗了。那末题目便去了,要怎样保存那个淌呢?
所幸JavaEE供给了1个HttpServletRequestWrapper类,从类实也能够晓得它是1个http央浼包拆器,其鉴于装束者形式杀青了HttpServletRequest界里。该类并不实正来杀青HttpServletRequest的办法,而不过正在办法内乱又来移用HttpServletRequest的办法,因此尔们能够经由过程担当该类并达成念要从头界说的办法以到达包拆本死HttpServletRequest对于象的目标。
起首尔们要界说1个容器,将输出淌内里的数据保存到那个容器里,那个容器能够是数组或者聚合。而后尔们誊写getInputStream办法,屡屡皆从那个容器里读数据,如许尔们的输出淌便能够读与任性次了。
packagecom.example.springboot.filter;importlombok.extern.slf4j.Slf4j;importjavax.servlet.ReadListener;importjavax.servlet.ServletInputStream;importjavax.servlet.ServletRequest;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletRequestWrapper;importjava.io.*;importjava.nio.charset.Charset;/***包拆HttpServletRequest,目标是让其输出淌可反复读**/@Slf4jpublicclassRequestWrapperextendsHttpServletRequestWrapper{/***保存body数据的容器*/privatefinalbyte[]body;publicRequestWrapper(HttpServletRequestrequest)throwsIOException{super(request);//将body数据保存起去StringbodyStr=getBodyString(request);body=bodyStr.getBytes(Charset.defaultCharset());}/***获得恳求Body*/publicStringgetBodyString(finalServletRequestrequest){try{returninputStream2String(request.getInputStream());}catch(IOExceptione){thrownewRuntimeException(e);}}/***获得哀求Body*/publicStringgetBodyString(){finalInputStreaminputStream=newByteArrayInputStream(body);returninputStream2String(inputStream);}/***将inputStream里的数据读与出去并更动成字符串*/privateStringinputStream2String(InputStreaminputStream){StringBuildersb=newStringBuilder();BufferedReaderreader=null;try{reader=newBufferedReader(newInputStreamReader(inputStream,Charset.defaultCharset()));Stringline;while((line=reader.readLine())!=null){sb.append(line);}}catch(IOExceptione){thrownewRuntimeException(e);}finally{if(reader!=null){try{reader.close();}catch(IOExceptione){log.error("",e);}}}returnsb.toString();}@OverridepublicBufferedReadergetReader()throwsIOException{returnnewBufferedReader(newInputStreamReader(getInputStream()));}@OverridepublicServletInputStreamgetInputStream()throwsIOException{finalByteArrayInputStreaminputStream=newByteArrayInputStream(body);returnnewServletInputStream(){@Overridepublicintread()throwsIOException{returninputStream.read();}@OverridepublicbooleanisFinished(){returnfalse;}@OverridepublicbooleanisReady(){returnfalse;}@OverridepublicvoidsetReadListener(ReadListenerreadListener){}};}}除要写1个包拆器中,尔们借须要正在过滤器里将本死的HttpServletRequest对于象调换成尔们的RequestWrapper对于象,代码以下:
packagecom.example.springboot.filter;importlombok.extern.slf4j.Slf4j;importjavax.servlet.*;importjavax.servlet.http.HttpServletRequest;importjava.io.IOException;/***调换HttpServletRequest**/@Slf4jpublicclassWordStrStreamFilterimplementsFilter{@Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{}@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{ServletRequestrequestWrapper=newRequestWrapper((HttpServletRequest)request);chain.doFilter(requestWrapper,response);}@Overridepublicvoiddestroy(){}}而后尔们便能够正在阻挡器中获得Body数据也没有慌Controller层会报错了:
packagecom.example.springboot.filter;importlombok.extern.slf4j.Slf4j;importorg.springframework.http.MediaType;importorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.servlet.ModelAndView;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;/***编写阻挡器**/@Slf4jpublicclassSignatureInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(isJson(request)){//获得json字符串StringjsonParam=newRequestWrapper(request).getBodyString();System.out.println("SignatureInterceptor:"+jsonParam);}returntrue;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{}/***判定原次吁请的数据典范能否为json*/privatebooleanisJson(HttpServletRequestrequest){if(request.getContentType()!=null){returnrequest.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)||request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);}returnfalse;}}过滤器战阻挡器正在设置类中停止挂号才会见效,过滤器摆设类代码以下:
packagecom.example.springboot.config;importcom.example.springboot.filter.WordStrStreamFilter;importorg.springframework.boot.web.servlet.FilterRegistrationBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.servlet.Filter;/***过滤器设置类**/@ConfigurationpublicclassFilterConfig{/***登记过滤器*/@BeanpublicFilterRegistrationBeansomeFilterRegistration(){FilterRegistrationBeanregistration=newFilterRegistrationBean();registration.setFilter(replaceStreamFilter());registration.addUrlPatterns("/*");registration.setName("streamFilter");returnregistration;}/***真例化StreamFilter*/@Bean(name="replaceStreamFilter")publicFilterreplaceStreamFilter(){returnnewWordStrStreamFilter();}}立案阻挡器
packagecom.example.springboot.config;importcom.example.springboot.filter.SignatureInterceptor;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;/***备案阻挡器**/@ConfigurationpublicclassInterceptorConfigimplementsWebMvcConfigurer{@BeanpublicSignatureInterceptorgetSignatureInterceptor(){returnnewSignatureInterceptor();}/***立案阻挡器*/@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(getSignatureInterceptor()).addPathPatterns("/**");}}编写尝试Controller
packagecom.example.springboot.controller;importorg.springframework.web.bind.annotation.*;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.util.Map;/***尝试XSS*/@RestController@RequestMapping("/test")publicclassXssController{@GetMapping("/xss")publicStringxssGet(@RequestParam("QueryData")StringQueryData,@RequestBodyMap<String,Object>body,HttpServletRequestrequest,HttpServletResponseresponse){System.out.println(QueryData);System.out.println(request.getHeader("HeaderData"));System.out.println(body);returnQueryData;}}收收尝试吁请
注重,正在理论中涌现,正在判定能否为JSON数据时
publicstaticfinalStringAPPLICATION_JSON_VALUE="application/json";publicstaticfinalStringAPPLICATION_JSON_UTF8_VALUE="application/json;charset=UTF-8";偶尔变量中央是带空格的,原因上述代码中的判定办法改成:
/***判定原次苦求的数据典范能否为json*/privatebooleanisJson(HttpServletRequestrequest){if(request.getContentType()!=null){returnrequest.getContentType().contains("application/json");}returnfalse;}END
推举您浏览更多相关于“ jsonspringboot阻挡器body过滤器HttpServletRequest ”的著作
文章推荐
Copyright © 2024-2025 燿动吧 – 知识分享,快乐你我,燿动青春 http://www.yaodong8.com All Rights Reserved 网站地图