0

With Spring Security is there anyway to add a custom controller to execute the POST when using loginProcessingUrl ? As far as I know this is tied to UsernamePasswordAuthenticationFilter.

My current security configuration looks like,

@Override
public void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http.csrf()
            .disable()
            .addFilterBefore(statelessCsrfFilter(), CsrfFilter.class);

    http
        .addFilterAfter(requestInterceptorFilter(), CsrfFilter.class)
        .authorizeRequests()
        .antMatchers(
            "/resources/**",
            "/css/**",
            "/img/**",
            "/js/**",
            "/webjars/**",
            "/management/**",
            "/api/v1/auth/login",
            "/api/v1/auth/logout",
            "/api/v1/auth/token",
            "/api/v1/auth/forgot-password",
            "/api/v1/auth/management/**"
        ).permitAll()
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/api/v1/auth/login")
        .loginProcessingUrl("/api/v1/auth/login")
        .permitAll()
        .failureHandler(failureHandler());
    // @formatter:on
}

The reason for all this is because when I use micrometer to pull the metrics I see the following exception,

2018-11-15 20:36:42.449 DEBUG [auth-service,,,] 43751 --- [ XNIO-2 task-14] i.m.s.web.servlet.WebMvcMetricsFilter    : Unable to time request

org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
    at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.handleNoMatch(RequestMappingInfoHandlerMapping.java:205)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:374)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:314)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:61)
    at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:352)
    at org.springframework.web.servlet.handler.HandlerMappingIntrospector.getMatchableHandlerMapping(HandlerMappingIntrospector.java:123)
    at io.micrometer.spring.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:65)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
    at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.SessionRestoringHandler.handleRequest(SessionRestoringHandler.java:119)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:336)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

My guess at this point is that micrometer's WebMvcMetricsFilter is tied to the Spring MVC filter chain and it isn't aware of Spring Security filter chain. I raised this as an issue #1011 but would like to know if I can get around this problem by declaring my own controller for the POST.

My PoC around this problem -> spring-boot-spring-security-micrometer

Any pointers around this is appreciated.

nixgadget
  • 6,983
  • 16
  • 70
  • 103
  • Just disable debug logging... This is a DEBUG message not an exception. – M. Deinum Nov 15 '18 at 23:41
  • It is an exception in the sense that no metrics will be logged for the `POST` – nixgadget Nov 15 '18 at 23:42
  • The main issue is that there apparently is a handler that is supporting that URL (maybe a catch all). However that doesn't support POST requests. So figure out what matches and why. – M. Deinum Nov 15 '18 at 23:47
  • Yup that handler is for the `GET` defined to get login page when `/api/v1/auth/login` is called. Obviously I have not defined a `POST` as this is handled internally by spring security. If that makes sense. – nixgadget Nov 15 '18 at 23:50
  • Yes it makes sense, but the issue you reported isn't. This has nothing to do with filter ordering or the fact that you use Spring security. This is the simple fact that there is a partial match. Now if you would make that a regular `@RequestMapping` it would, kind of work, but your metrics name would be wrong. The same would be the case if you add a regular view controller through the `ViewControllerRegistry` instead of creating an `@Controller`. – M. Deinum Nov 16 '18 at 00:00
  • From the debugging I've done so far I can see that the partial match is due to the `@RequestMapping(value = "/api/v1/auth/login", method = RequestMethod.GET)` in my controller. But yes i get your point about the metrics being wrong. – nixgadget Nov 16 '18 at 00:05
  • Is the only option at this point to just ignore this error ? – nixgadget Nov 16 '18 at 00:11
  • I think you try to call a get rest methode with post and it gives you that exception (HttpRequestMethodNotSupportedException) – Abder KRIMA Nov 16 '18 at 02:00
  • @TinyOS like I've mentioned earlier its Spring Security doing the `POST` via one of its handlers. Ive got no control over it. I've pretty much exhausted my knowledge around it so waiting on someone from the micrometer team to answer the question. – nixgadget Nov 16 '18 at 04:34
  • You are doing the POST not Spring Security. It is just an incoming request. As stated it partially matches your GET mapping (hence the error POST not supported). Change that to a `@RequestMapping` and it kind of works. – M. Deinum Nov 16 '18 at 08:09
  • @M.Deinum heres a PoC to proof that there is something missing with micrometer https://github.com/nixgadget/spring-boot-spring-security-micrometer – nixgadget Nov 17 '18 at 01:50

1 Answers1

0

As described in the comments above, this issue actually stems from Spring Security (tested on Spring Boot 1.5.x as given in PoC spring-boot-spring-security-micrometer).

Hence, this will be addressed by micrometer issue #15204.

nixgadget
  • 6,983
  • 16
  • 70
  • 103