My goal is to log the incoming http requests to my Spring (5.0.7) MVC Web / Spring security (4.2.3) application. I want to save the requestdata in a database, containing IP, request method, headers, body and the URL. The critical requests are the login attempts so I need to fetch the POST request to the /login URL. Therefore I wrote a filter to get this done because an interceptor is applied after the filter chain.
I looked at the solution at this SO question and I also tried the variant with an interceptor.
WebAppInitializer
public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
...
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
...
filterRegistration = servletContext.addFilter("logFilter", new APILoggingFilter() );
String[] mappings = new String[] {"/login", "/logout", "/data"};
filterRegistration.addMappingForUrlPatterns(null, false, mappings);
}
}
LoggingFilter
public class APILoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
boolean isFirstRequest = !isAsyncDispatch(request);
HttpServletRequest requestToUse = request;
HttpServletResponse responseToUse = response;
if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
requestToUse = new ContentCachingRequestWrapper(request);
}
if (isFirstRequest && !(response instanceof ContentCachingResponseWrapper)) {
responseToUse = new ContentCachingResponseWrapper(response);
}
filterChain.doFilter(requestToUse, responseToUse);
String user = SecurityContextHolder.getContext().getAuthentication().getName();
// This is were the logging to the database should take place
if (!isAsyncStarted(request)) {
ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(responseToUse, ContentCachingResponseWrapper.class);
responseWrapper.copyBodyToResponse();
}
}
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
}
log4j.properties
log4j.logger.org.springframework=INFO
log4j.logger.org.springframework.web.filter=DEBUG
With this code I am able to log all request to the database with almost all the data I wanted. I see GET requests and POST requests.
The problem or question is: why is it not possible to see the username? I tried to get the username via
request.getRemoteUser();
and with
String user = SecurityContextHolder.getContext().getAuthentication().getName();
It is always null. And here is the curious thing. If I disable the second entry in my log4j.properties (log4j.logger.org.springframework.web.filter=DEBUG) then I always get the username with both options BUT I never fetch a POST request anymore only GET requests.
How do I achieve both goals? Fetch all requests AND get the username?