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.