From d76fb7f2e621fe127e052b0e0e591ed38a49dd66 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:58:13 -0600 Subject: [PATCH] Polish WebAttributes ApplicationContext Support Closes gh-8843 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com> --- .../taglibs/authz/AbstractAuthorizeTag.java | 14 ++++++----- .../authz/AbstractAuthorizeTagTests.java | 23 +++++++++++-------- .../security/web/WebAttributes.java | 16 +++++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java index f70dcd78ec..6785ed6458 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java @@ -62,8 +62,6 @@ import org.springframework.util.StringUtils; */ public abstract class AbstractAuthorizeTag { - private static final String DISPATCHER_SERVLET_CONTEXT_ATTRIBUTE = "org.springframework.web.servlet.DispatcherServlet.CONTEXT"; - @SuppressWarnings("NullAway.Init") private @Nullable String access; @@ -226,11 +224,15 @@ public abstract class AbstractAuthorizeTag { } private ApplicationContext getApplicationContext() { - Object dispatcherContext = getRequest().getAttribute(DISPATCHER_SERVLET_CONTEXT_ATTRIBUTE); - if (dispatcherContext instanceof ApplicationContext applicationContext) { - return applicationContext; + Object value = getRequest().getAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE); + if (value == null) { + return SecurityWebApplicationContextUtils.findRequiredWebApplicationContext(getServletContext()); } - return SecurityWebApplicationContextUtils.findRequiredWebApplicationContext(getServletContext()); + if (value instanceof ApplicationContext context) { + return context; + } + throw new IllegalArgumentException("WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE value must be of type " + + "ApplicationContext, found type " + value.getClass()); } } diff --git a/taglibs/src/test/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTagTests.java b/taglibs/src/test/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTagTests.java index 1037dd9e28..a55d87406d 100644 --- a/taglibs/src/test/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTagTests.java +++ b/taglibs/src/test/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTagTests.java @@ -42,6 +42,7 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; @@ -136,20 +137,24 @@ public class AbstractAuthorizeTagTests { @Test @SuppressWarnings("rawtypes") - public void expressionFromDispatcherContextWhenRootContextPresent() throws IOException { + public void expressionWhenApplicationContextAttributeIsSetThenUsed() throws IOException { SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "USER")); - WebApplicationContext root = mock(WebApplicationContext.class); - given(root.getBeansOfType(SecurityExpressionHandler.class)).willReturn(Collections.emptyMap()); - given(root.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]); - this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, root); DefaultWebSecurityExpressionHandler expected = new DefaultWebSecurityExpressionHandler(); - WebApplicationContext dispatcher = mock(WebApplicationContext.class); - given(dispatcher.getBeansOfType(SecurityExpressionHandler.class)) + WebApplicationContext context = mock(WebApplicationContext.class); + given(context.getBeansOfType(SecurityExpressionHandler.class)) .willReturn(Collections.singletonMap("wipe", expected)); - given(dispatcher.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]); - this.request.setAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", dispatcher); + given(context.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]); + this.request.setAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE, context); this.tag.setAccess("permitAll"); assertThat(this.tag.authorize()).isTrue(); + verify(context).getBeansOfType(SecurityExpressionHandler.class); + } + + @Test + public void expressionWhenApplicationContextAttributeIsWrongTypeThenIllegalArgumentException() { + this.request.setAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE, "notAnApplicationContext"); + this.tag.setAccess("permitAll"); + assertThatIllegalArgumentException().isThrownBy(() -> this.tag.authorize()); } private class AuthzTag extends AbstractAuthorizeTag { diff --git a/web/src/main/java/org/springframework/security/web/WebAttributes.java b/web/src/main/java/org/springframework/security/web/WebAttributes.java index 3ed65f0053..f0e849ea80 100644 --- a/web/src/main/java/org/springframework/security/web/WebAttributes.java +++ b/web/src/main/java/org/springframework/security/web/WebAttributes.java @@ -37,6 +37,22 @@ public final class WebAttributes { */ public static final String ACCESS_DENIED_403 = "SPRING_SECURITY_403_EXCEPTION"; + /** + * Set as a request attribute to provide an + * {@link org.springframework.context.ApplicationContext} for use by JSP authorize + * tags when resolving security beans. + *

+ * When set, this attribute is preferred over the root web application context. The + * value must be of type {@link org.springframework.context.ApplicationContext}. + * + *

+ * Used in {@code org.springframework.security.taglibs.authz.AbstractAuthorizeTag}. + * + * @since 7.1 + */ + public static final String APPLICATION_CONTEXT_ATTRIBUTE = WebAttributes.class.getName() + + ".APPLICATION_CONTEXT_ATTRIBUTE"; + /** * Used to cache an authentication-failure exception in the session. *