If a WebSecurityConfiguration contains EndpointRequestMatchers, for example like this

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll()
                .anyRequest().permitAll();
    }
}

and the application uses thymeleaf templates with sec: tags - like this:

<div sec:authorize-url="/admin"> /admin is accessable</div>

any http-invocation adressing this template throws:

java.lang.UnsupportedOperationException: public abstract javax.servlet.ServletContext javax.servlet.ServletRequest.getServletContext() is not supported
    at org.springframework.security.web.FilterInvocation$UnsupportedOperationExceptionInvocationHandler.invoke(FilterInvocation.java:304) ~[spring-security-web-5.5.1.jar:5.5.1]
    at com.sun.proxy.$Proxy93.getServletContext(Unknown Source) ~[na:na]
    at javax.servlet.ServletRequestWrapper.getServletContext(ServletRequestWrapper.java:369) ~[tomcat-embed-core-9.0.50.jar:4.0.FR]
    at org.springframework.boot.security.servlet.ApplicationContextRequestMatcher.matches(ApplicationContextRequestMatcher.java:58) ~[spring-boot-2.5.3.jar:2.5.3]
    at org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource.getAttributes(DefaultFilterInvocationSecurityMetadataSource.java:84) ~[spring-security-web-5.5.1.jar:5.5.1]
    at org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator.isAllowed(DefaultWebInvocationPrivilegeEvaluator.java:87) ~[spring-security-web-5.5.1.jar:5.5.1]
    at org.thymeleaf.extras.springsecurity5.auth.AuthUtils$MvcAuthUtils.authorizeUsingUrlCheckMvc(AuthUtils.java:362) ~[thymeleaf-extras-springsecurity5-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at org.thymeleaf.extras.springsecurity5.auth.AuthUtils$MvcAuthUtils.access$300(AuthUtils.java:304) ~[thymeleaf-extras-springsecurity5-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at org.thymeleaf.extras.springsecurity5.auth.AuthUtils.authorizeUsingUrlCheck(AuthUtils.java:236) ~[thymeleaf-extras-springsecurity5-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at org.thymeleaf.extras.springsecurity5.dialect.processor.AuthorizeUrlAttrProcessor.isVisible(AuthorizeUrlAttrProcessor.java:74) ~[thymeleaf-extras-springsecurity5-3.0.4.RELEASE.jar:3.0.4.RELEASE]
    at org.thymeleaf.standard.processor.AbstractStandardConditionalVisibilityTagProcessor.doProcess(AbstractStandardConditionalVisibilityTagProcessor.java:61) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:661) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072) ~[thymeleaf-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:366) ~[thymeleaf-spring5-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:190) ~[thymeleaf-spring5-3.0.12.RELEASE.jar:3.0.12.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1397) ~[spring-webmvc-5.3.9.jar:5.3.9]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1142) ~[spring-webmvc-5.3.9.jar:5.3.9]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081) ~[spring-webmvc-5.3.9.jar:5.3.9]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.9.jar:5.3.9]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.9.jar:5.3.9]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.9.jar:5.3.9]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.50.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.9.jar:5.3.9]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.50.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) ~[tomcat-embed-core-9.0.50.jar:9.0.50]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) ~[tomcat-embed-core-9.0.50.jar:9.0.50]

The reason: EndpointRequestMatcher is a subclass of ApplicationContextRequestMatcher and the matches Method requires a HttpServletRequest with a valid servletContext (to obtain the applicationcontext). But the concrete HttpServletRequest is an instance of FilterInvocation.DummyRequest which doesn't support 'getServletContext()'.

This is a small SpringBoot Application (web, actuator, spring-security and thymleaf) to demonstrate the problem: https://github.com/tvahrst/springboot-security-thymeleaf

URL: localhost:8080/main

Comment From: wilkinsona

Thanks for the report but I don't think there's anything that we can do about this in Spring Boot. As you have described, we need the ServletContext so that we can get the current application context. There's nothing in Spring Security's RequestMatcher interface that states that getServletContext() isn't supported within the scope of a matches call so I don't think we're doing anything wrong here. I'll ask the Security team to take a look.

Comment From: marcusdacoregio

Hi @tvahrst and @wilkinsona, thanks for the report.

Would you mind opening an issue in the Spring Security repository?

Once you open the issue there, you can tag me so we can continue the discussion.

Comment From: tvahrst

I thought, this might be a Spring Boot issue, because using an Spring Security AntPathMatcher instead of Spring Boot EndpointRequestMatcher solves the problem...

Current workaround:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/actuator/health").permitAll()
//                .requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll()
                .anyRequest().permitAll();
    }

Comment From: wilkinsona

Thanks, @marcusdacoregio.

@tvahrst, that's to be expected as antMatchers doesn't rely on being able to access the servlet context. The downside is that you have had to hardcode the path rather than it being derived from the endpoint and the actuator's configuration. Doing that requires access to the application context.

If you'd like to pursue this, please open a Spring Security issue as requested. If you do so, please mention me on it as I'd like to follow the discussion as this problem affects every ApplicationContextRequestMatcher.

Comment From: philwebb

I've opened https://github.com/spring-projects/spring-security/issues/10208. I think we either a new method on WebInvocationPrivilegeEvaluator is required and Thymeleaf needs to make use of it, or Spring Security needs to setup a WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE request attribute.