0

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?

M46
  • 923
  • 9
  • 20
  • because you need to read the section about filters in the spring security documentation. Where do you think your filter is plaaced when you are registering it manually? Read the spring boot/spring security official documentation how to actually registering a filter. – Toerktumlare Aug 02 '22 at 16:20
  • @Toerktumlare Please read what I have written! The problem is not to register a filter! The filter is the first in the filter chain so every request gets in here. The proble is the log4j setting which causes trouble. – M46 Aug 03 '22 at 07:09
  • read what i have written, you are trying to log info about who is authenticated, but if your filter is first in the chain no one has been authenticated yet since authentication and the population of the SecurityContext is done in a later filter.. That is my exact point, your filter is the first in the chain. – Toerktumlare Aug 03 '22 at 08:24
  • This is true, my filter is first but then it must be able to see POST requests coming in but it's not due to the log4j setting. And the logging is done after walking through the complete filter chain. As you can see I have added "filterChain.doFilter(requestToUse, responseToUse);" before the logging so authentication is completed at that point. – M46 Aug 03 '22 at 08:29
  • The last filter in the spring security chain clears the context to prepare for the next request. As you are saying, you are logging AFTER the entire chain, so the context has been cleared at that point – Toerktumlare Aug 03 '22 at 09:54

0 Answers0