Describe the bug

Spring Security version 6.5.0 release.

Since the AntPathMatcher is deprecated, I replaced it with PathPatternRequestMatcher expecting that it would behave the same. However I discovered that the PathPatternRequestMatcher caches the parsed RequestPath in a request attribute (see org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher#getPathContainer). This leads to unexpected behavior when forwarding.

For example if you make a request to /path-a then call request.getRequestDispatcher("/path-b").forward(request, response) then a PathPatternRequestMatcher will never match on /path-b (assuming that /path-a was cached in the request, in my case by a filter earlier in the chain which uses a PathPatternRequestMatcher).

To Reproduce

  • Make request to /path-a
  • Use any PathPatternRequestMatcher on the request e.g. PathPatternRequestMatcher.withDefaults().matcher("/path-a").matches(request)
  • Forward the request request.getRequestDispatcher("/path-b").forward(request, response)
  • After forwarding PathPatternRequestMatcher.withDefaults().matcher("/path-b").matches(request) will return false

Expected behavior

The PathPatternRequestMatcher should behave the same as the AntPathMatcher which it is replacing and always match on the jakarta.servlet.http.HttpServletRequest#getRequestURI.

Sample

Can supply if required.

Screenshot

Screenshot illustrating the issue

Image

Comment From: jazdw

@jzheaux I also noticed that org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher#servletPath is never written, org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher#method could be final and initialized via the constructor also.

Comment From: jazdw

Another tidbit I just discovered is that org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityPathPatternParserBeanDefinitionRegistryPostProcessor now adds a org.springframework.web.filter.ServletRequestPathFilter before the spring security filter chain. This is not mentioned in the 6.5.0 release notes.

Comment From: jazdw

You can prevent the automatic registration of the ServletRequestPathFilter filter by overriding the bean:

    @Bean
    static BeanDefinitionRegistryPostProcessor springSecurityPathPatternParserBeanDefinitionRegistryPostProcessor() {
        // noop post processor
        return registry -> {};
    }

I think the reason we are encountering this issue in our application is that we register the DelegatingFilterProxy using our own initializer and it does not handle jakarta.servlet.DispatcherType#FORWARD. Our workaround is to disable the BeanDefinitionRegistryPostProcessor as above, then register our own ServletRequestPathFilter which does handle the FORWARD dispatch.

Comment From: jzheaux

Thanks for the detail, @jazdw. Are you able to provide a sample app that reproduces the issue?