-
-
Save calo81/2071634 to your computer and use it in GitHub Desktop.
package com.paddypower.financials.market.management.rest.logging; | |
import java.io.BufferedReader; | |
import java.io.ByteArrayInputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import javax.servlet.Filter; | |
import javax.servlet.FilterChain; | |
import javax.servlet.FilterConfig; | |
import javax.servlet.ServletException; | |
import javax.servlet.ServletInputStream; | |
import javax.servlet.ServletRequest; | |
import javax.servlet.ServletResponse; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletRequestWrapper; | |
import org.apache.commons.io.IOUtils; | |
public class LoggerFilter implements Filter { | |
private Auditor auditor; | |
public void destroy() { | |
// Nothing to do | |
} | |
public void doFilter(ServletRequest request, ServletResponse response, | |
FilterChain chain) throws IOException, ServletException { | |
ResettableStreamHttpServletRequest wrappedRequest = new ResettableStreamHttpServletRequest( | |
(HttpServletRequest) request); | |
// wrappedRequest.getInputStream().read(); | |
String body = IOUtils.toString(wrappedRequest.getReader()); | |
auditor.audit(wrappedRequest.getRequestURI(),wrappedRequest.getUserPrincipal(), body); | |
wrappedRequest.resetInputStream(); | |
chain.doFilter(wrappedRequest, response); | |
} | |
public void init(FilterConfig arg0) throws ServletException { | |
// Nothing to do | |
} | |
private static class ResettableStreamHttpServletRequest extends | |
HttpServletRequestWrapper { | |
private byte[] rawData; | |
private HttpServletRequest request; | |
private ResettableServletInputStream servletStream; | |
public ResettableStreamHttpServletRequest(HttpServletRequest request) { | |
super(request); | |
this.request = request; | |
this.servletStream = new ResettableServletInputStream(); | |
} | |
public void resetInputStream() { | |
servletStream.stream = new ByteArrayInputStream(rawData); | |
} | |
@Override | |
public ServletInputStream getInputStream() throws IOException { | |
if (rawData == null) { | |
rawData = IOUtils.toByteArray(this.request.getReader()); | |
servletStream.stream = new ByteArrayInputStream(rawData); | |
} | |
return servletStream; | |
} | |
@Override | |
public BufferedReader getReader() throws IOException { | |
if (rawData == null) { | |
rawData = IOUtils.toByteArray(this.request.getReader()); | |
servletStream.stream = new ByteArrayInputStream(rawData); | |
} | |
return new BufferedReader(new InputStreamReader(servletStream)); | |
} | |
private class ResettableServletInputStream extends ServletInputStream { | |
private InputStream stream; | |
@Override | |
public int read() throws IOException { | |
return stream.read(); | |
} | |
} | |
} | |
public void setAuditor(Auditor auditor) { | |
this.auditor = auditor; | |
} | |
} |
I've been using this for a while now (thank you!) but recently I discovered that it is messing up character encoding for binary file uploading. I'm trying to pin-point where this is happening, but if anyone else is more InputStream savvy than me I would appreciate the help!
In dofilter method I am trying to get Authenticaed user by using SecurityContextHolder, but it is coming null, Although it is working in Spring MVC. What I am trying to use is this
if (SecurityContextHolder.getContext().getAuthentication()!=null) {
principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.print(SecurityContextHolder.getContext().getAuthentication());
}
But here SecurityContextHolder.getContext().getAuthentication() is coming null, so I am not able to get my principal from here, Can you please help me out
@sabela85 because body data is stored in stream. Stream could only be read once according to Javadoc
It seems that both getInputStream
and getReader
are not called under Spring 4, any ideas?
Please its urgent,i m using spring boot .i use filter as interceptor and i got this messgae
"timestamp": 1450168414601,
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "Required request body content is missing: org.springframework.web.method.HandlerMethod$HandlerMethodParameter@21779fb",
"path": "/BootMyBatisDemo/student/create"
}
It is really useful, but do I need to close the ByteArrayInputStream created in getReader method?
Very useful! Thanks
Very useful.
Awesome! Thanks for documenting this.
This has potential issues with OOM if the request is too large, such as a large file upload. There should be limits on the how much content is recorded from the input stream
Know its been long this thread has been stale. Much appreciate if someone can help. What is the Auditor class used here ? can you please let me know. I am writing a similar filter function to decode a header value.[At the Controller level request header param can only be retreived by getHeader() which by decode the value in some default encoding scheme.]
what about response filter, i want to catch or log all the data of webservice's response before it reach to client
someone have experience on it pleas share it bro... :)
Thank you so much for this!
And hope my version is helpful to someone
public class ResponseFilter implements Filter{
public ResponseFilter() {
super();
}
private static class ResettableStreamHttpServletResponse extends HttpServletResponseWrapper {
private HttpServletResponse response;
public ResettableStreamHttpServletResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
}
@Override public void init(FilterConfig config) throws ServletException{}
@Override public void destroy(){}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
ResettableStreamHttpServletResponse wrappedResponse = new ResettableStreamHttpServletResponse((HttpServletResponse) response);
wrappedResponse.addHeader("Access-Control-Allow-Origin", "*");
wrappedResponse.addHeader("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
wrappedResponse.addHeader("Access-Control-Allow-Credentials", "true");
wrappedResponse.addHeader("Access-Control-Allow-Methods", "GET, POST");
chain.doFilter(request, wrappedResponse);
}
}
Concerning the encoding problem, this might work, but I haven't tried it:
public class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {
private byte[] rawData;
private HttpServletRequest request;
private ResettableServletInputStream servletStream;
ResettableStreamHttpServletRequest(HttpServletRequest request) {
super(request);
this.request = request;
this.servletStream = new ResettableServletInputStream();
}
void resetInputStream() {
servletStream.stream = new ByteArrayInputStream(rawData);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getInputStream());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return servletStream;
}
@Override
public BufferedReader getReader() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getInputStream());
servletStream.stream = new ByteArrayInputStream(rawData);
}
String encoding = getCharacterEncoding();
if (encoding != null) {
return new BufferedReader(new InputStreamReader(servletStream, encoding));
} else {
return new BufferedReader(new InputStreamReader(servletStream));
}
}
private class ResettableServletInputStream extends ServletInputStream {
private InputStream stream;
@Override
public int read() throws IOException {
return stream.read();
}
}
}
This is awesome !!
Thanks. The following code with some changes from your code works for me :
private static class ResettableStreamHttpServletRequest extends HttpServletRequestWrapper {
private byte[] rawData;
private HttpServletRequest request;
private ResettableServletInputStream servletStream;
private ResettableStreamHttpServletRequest(HttpServletRequest request) {
super(request);
this.request = request;
servletStream = new ResettableServletInputStream();
}
public void resetInputStream() {
servletStream.stream = new ByteArrayInputStream(rawData);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return servletStream;
}
@Override
public BufferedReader getReader() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return new BufferedReader(new InputStreamReader(servletStream));
}
}
private static class ResettableServletInputStream extends ServletInputStream {
private InputStream stream;
@Override
public int read() throws IOException {
return stream.read();
}
@Override
public boolean isFinished() {
try {
int available = stream.available();
return available == 0;
} catch (IOException e) {
return true;
}
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) { }
}
Thanks ! Really useful !!! 😄
I am calling Http POST to the REST endpoint, but making XML request, in my case 3rd party system can only make the SOAP request, now I need to remove soapenv:Envelope from the request and only pass the xml tags. I am using Spring Boot application.
Is there any way to read the request and update the POST data?
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:wer.com:dms:wsdls:organization">
<soapenv:Header/>
<soapenv:Body>
<urn:getProfile>
<getProfileRequest>
<patientId>160166</patientId>
</getProfileRequest>
</urn:getProfile>
</soapenv:Body>
</soapenv:Envelope>
Thank you.
It was really helpful.
Here is my working solution, that is up-to-date with packages' versions.
private static final class CopyableHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] rawData;
private HttpServletRequest request;
private CopyableServletInputStream servletInputStream;
private static final class CopyableServletInputStream extends ServletInputStream {
private InputStream stream;
public CopyableServletInputStream(InputStream stream) {
this.stream = stream;
}
@Override
public boolean isFinished() {
try {
int remainingBytes = stream.available();
return 0 == remainingBytes;
} catch (IOException ex) {
return false;
}
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) { }
@Override
public int read() throws IOException {
return stream.read();
}
}
private CopyableHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
copyBodyDataIfNecessary();
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
copyBodyDataIfNecessary();
return new BufferedReader(new InputStreamReader(servletInputStream));
}
public void copyBodyDataIfNecessary() throws IOException {
if(null == rawData) {
rawData = IOUtils.toByteArray(request.getReader(), "utf-8");
servletInputStream = new CopyableServletInputStream(new ByteArrayInputStream(rawData));
}
}
public void resetStream() throws IOException {
this.servletInputStream = new CopyableServletInputStream(new ByteArrayInputStream(rawData));
}
}
very good!!!
Thank you every one, a little change because not working in my project by a cast wrong
private static final class CopyableHttpServletRequestWrapper extends HttpServletRequestWrapper {
private byte[] rawData;
private final HttpServletRequest request;
private CopyableServletInputStream servletInputStream;
private static final class CopyableServletInputStream extends ServletInputStream {
private final InputStream stream;
public CopyableServletInputStream(InputStream stream) {
this.stream = stream;
}
@Override
public boolean isFinished() {
try {
int remainingBytes = stream.available();
return 0 == remainingBytes;
} catch (IOException ex) {
return false;
}
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return stream.read();
}
}
private CopyableHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
copyBodyDataIfNecessary();
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
copyBodyDataIfNecessary();
return new BufferedReader(new InputStreamReader(servletInputStream));
}
public void copyBodyDataIfNecessary() throws IOException {
if (null == rawData) {
rawData = IOUtils.toByteArray(this.request.getInputStream());
servletInputStream = new CopyableServletInputStream(new ByteArrayInputStream(rawData));
}
}
public void resetStream() throws IOException {
this.servletInputStream = new CopyableServletInputStream(new ByteArrayInputStream(rawData));
}
}
When you read the content of a request, you access its InputStream object and that InputStream cannot be reset to its initial position to re-read the content of the request. It's simply not possible, you can't read the content twice so apparently by making a request wrapper you essentially get a duplicate request which leaves the original request alone.