title: Spring unit test MockMvc fails when using custom filter in Spring Security
body: <p>I have a web application that should only be callable from specific IP addresses. Other than that, there is no need for authentication or for authorization; if you're coming from the right IP, you can see everything.</p>
  
  <p>To that end, searching StackOverflow and other places, I found a number of suggestions for filtering requests by IP address in Spring Security. They all took this form (extending WebSecurityConfigurerAdapter using java configuration):</p>
  
  <pre><code>http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')");
  </code></pre>
  
  <p>However, that never worked for me; it never rejected any request, no matter what IP address I made the request from. Instead, I implemented my IP filtering with a custom filter like this:</p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @PropertySource("classpath:config.properties")
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
      private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
  
      @Autowired
      private Environment env;
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          String ipRange = env.getRequiredProperty("trusted_ips");
          logger.info("@@@@@ SETTING UP SECURITY CONFIGURATION @@@@@@@@@@@@@");
          logger.info("@@ trusted_ips: " + ipRange);
          logger.info("@@@@@ SETTING UP SECURITY CONFIGURATION - END @@@@@@@@@@@@@");
          http.addFilterBefore(new IPSecurityFilter(ipRange), J2eePreAuthenticatedProcessingFilter.class)
              .authorizeRequests().antMatchers("/**").permitAll();
      }
  }
  </code></pre>
  
  <p>My IPSecurityFilter:</p>
  
  <pre><code>public class IPSecurityFilter extends OncePerRequestFilter {
      private static final Logger logger = LoggerFactory.getLogger(IPSecurityFilter.class);
      private String[] ipAddresses;
  
      public IPSecurityFilter(String strIPAddresses) {
          logger.info("@@@@ Our IP Address whitelist: " + strIPAddresses);
          this.ipAddresses = strIPAddresses.split(",");
      }
  
      @Override
      protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
          logger.info("Checking whether request should be allowed: " + request.getRequestURI());
          logger.info("@@@ Request is coming from IP address: " + request.getRemoteAddr());
          for (String ipAddress : ipAddresses) {
              if (ipAddress.equals(request.getRemoteAddr())) {
                  logger.info("@@@ Allowing request from ip address: " + request.getRemoteAddr());
                  return;         // We accept requests from this IP address
              }
          }
          // The remote IP address isn't on our white list; throw an exception
          throw new AccessDeniedException("Access has been denied for your IP address: " + request.getRemoteAddr());
      }
  }
  </code></pre>
  
  <p>This seems to work in that the request is rejected if it originates from an IP Address that isn't on my white list.</p>
  
  <p>However, with this configuration, my unit (using MockMvc) test fails; and it fails in a way that I would never have expected. When the unit test runs, it appears to use the Spring Security configuration correctly and the request passes the security test (the IP white list includes 127.0.0.1 and according to the log that is generated while the test is being run, the request is coming from that IP). However, the request never seems to be routed to my controller.</p>
  
  <p>Here is my test:</p>
  
  <pre><code>@RunWith(SpringRunner.class)
  @WebMvcTest()
  //@WebMvcTest(value = HandlerController.class)
  @AutoConfigureMockMvc
  @Import(SecurityConfig.class)
  public class HandlerControllerTest {
  
      @Autowired
      private MockMvc mvc;
  
      @Test
      public void getIndex() throws Exception {
          mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
             .andExpect(status().isOk())
             .andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
      }
  }
  </code></pre>
  
  <p>And finally, here is my controller (please ignore the idiotic way that I'm generating the JSON return value, it's still very early in development):</p>
  
  <pre><code>@RestController
  public class HandlerController {
      private static final Logger logger = LoggerFactory.getLogger(HandlerController.class);
  
      @RequestMapping("/")
      public String index() {
          logger.info("### handling a request for / ###");
          return "{\"services\":[\"OutboundMessageService\"]}";
      }
  }
  </code></pre>
  
  <p>And here are the test results:</p>
  
  <pre><code>2017-11-14 08:29:12.151  INFO 25412 --- [           main] c.z.s.controllers.HandlerControllerTest  : Starting HandlerControllerTest on 597NLL1 with PID 25412 (started by User in C:\Development\KnowledgeBin\NewArchitecture\OutboundMessageHandler)
  2017-11-14 08:29:12.152  INFO 25412 --- [           main] c.z.s.controllers.HandlerControllerTest  : No active profile set, falling back to default profiles: default
  2017-11-14 08:29:12.178  INFO 25412 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
  2017-11-14 08:29:13.883  INFO 25412 --- [           main] b.a.s.AuthenticationManagerConfiguration : 
  
  Using default security password: 56e3fab8-f7fb-4fbd-b2d2-e37eae8cef5e
  
  2017-11-14 08:29:13.962  INFO 25412 --- [           main] c.z.services.security.IPSecurityFilter   : @@@@ Our IP Address whitelist: 122.22.22.22,127.0.0.1
  2017-11-14 08:29:14.086  INFO 25412 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3f4f9acd, org.springframework.security.web.context.SecurityContextPersistenceFilter@470a9030, org.springframework.security.web.header.HeaderWriterFilter@60c16548, org.springframework.security.web.csrf.CsrfFilter@435ce306, org.springframework.security.web.authentication.logout.LogoutFilter@607b2792, com.zpaper.services.security.IPSecurityFilter@46baf579, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@27494e46, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@36453307, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4bf324f9, org.springframework.security.web.session.SessionManagementFilter@452c8a40, org.springframework.security.web.access.ExceptionTranslationFilter@39ce27f2, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5767b2af]
  2017-11-14 08:29:14.183  INFO 25412 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.index()
  2017-11-14 08:29:14.184  INFO 25412 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/OutboundMessageService]}" onto public java.lang.String com.zpaper.services.controllers.HandlerController.outboundMessage()
  2017-11-14 08:29:14.189  INFO 25412 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity&lt;java.util.Map&lt;java.lang.String, java.lang.Object&gt;&gt; org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
  2017-11-14 08:29:14.190  INFO 25412 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
  2017-11-14 08:29:14.243  INFO 25412 --- [           main] c.z.s.config.HandlerWebConfiguration     : #### My Configuration handler was called ####
  2017-11-14 08:29:14.253  INFO 25412 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler]
  2017-11-14 08:29:14.313  INFO 25412 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext@209da20d: startup date [Tue Nov 14 08:29:12 MST 2017]; root of context hierarchy
  2017-11-14 08:29:14.784  INFO 25412 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring FrameworkServlet ''
  2017-11-14 08:29:14.784  INFO 25412 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet '': initialization started
  2017-11-14 08:29:14.805  INFO 25412 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : FrameworkServlet '': initialization completed in 21 ms
  
  2017-11-14 08:29:14.897  INFO 25412 --- [           main] c.z.s.controllers.HandlerControllerTest  : Started HandlerControllerTest in 3.095 seconds (JVM running for 3.995)
  2017-11-14 08:29:14.981  INFO 25412 --- [           main] c.z.services.security.IPSecurityFilter   : Checking whether request should be allowed: /
  2017-11-14 08:29:14.981  INFO 25412 --- [           main] c.z.services.security.IPSecurityFilter   : @@@ Request is coming from IP address: 127.0.0.1
  2017-11-14 08:29:14.981  INFO 25412 --- [           main] c.z.services.security.IPSecurityFilter   : @@@ Allowing request from ip address: 127.0.0.1
  
  MockHttpServletRequest:
        HTTP Method = GET
        Request URI = /
         Parameters = {}
            Headers = {Accept=[application/json]}
  
  Handler:
               Type = null
  
  Async:
      Async started = false
       Async result = null
  
  Resolved Exception:
               Type = null
  
  ModelAndView:
          View name = null
               View = null
              Model = null
  
  FlashMap:
         Attributes = null
  
  MockHttpServletResponse:
             Status = 200
      Error message = null
            Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
       Content type = null
               Body = 
      Forwarded URL = null
     Redirected URL = null
            Cookies = []
  Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.363 sec &lt;&lt;&lt; FAILURE! - in com.zpaper.services.controllers.HandlerControllerTest
  getIndex(com.zpaper.services.controllers.HandlerControllerTest)  Time elapsed: 0.12 sec  &lt;&lt;&lt; ERROR!
  org.json.JSONException: Unparsable JSON string: 
      at org.skyscreamer.jsonassert.JSONParser.parseJSON(JSONParser.java:42)
  </code></pre>
  
  <p>As can be seen in the log messages, the IP filter is being invoked and is allowing the request to continue. However, the debug string that is being emitted in my handler is nowhere to be seen and the return body is blank. Can anyone tell me why my security filter would prevent MockMvc from having its request successfully routed to my controller?</p>
  
  <p><strong>Final Note:</strong> if I use the http.authorizeRequests().anyRequest().access("hasIpAddress('127.0.0.1/0')"); configuration that I first listed or completely get rid of Spring Security by removing my SecurityConfig class, the request is routed successfully to my handler.</p>