1
0
mirror of synced 2026-05-22 13:23:17 +00:00

Polish WebAttributes ApplicationContext Support

Closes gh-8843

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
This commit is contained in:
Josh Cummings
2026-03-20 15:58:13 -06:00
parent 846794d31b
commit d76fb7f2e6
3 changed files with 38 additions and 15 deletions
@@ -62,8 +62,6 @@ import org.springframework.util.StringUtils;
*/ */
public abstract class AbstractAuthorizeTag { public abstract class AbstractAuthorizeTag {
private static final String DISPATCHER_SERVLET_CONTEXT_ATTRIBUTE = "org.springframework.web.servlet.DispatcherServlet.CONTEXT";
@SuppressWarnings("NullAway.Init") @SuppressWarnings("NullAway.Init")
private @Nullable String access; private @Nullable String access;
@@ -226,11 +224,15 @@ public abstract class AbstractAuthorizeTag {
} }
private ApplicationContext getApplicationContext() { private ApplicationContext getApplicationContext() {
Object dispatcherContext = getRequest().getAttribute(DISPATCHER_SERVLET_CONTEXT_ATTRIBUTE); Object value = getRequest().getAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE);
if (dispatcherContext instanceof ApplicationContext applicationContext) { if (value == null) {
return applicationContext; 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());
} }
} }
@@ -42,6 +42,7 @@ import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat; 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.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
@@ -136,20 +137,24 @@ public class AbstractAuthorizeTagTests {
@Test @Test
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public void expressionFromDispatcherContextWhenRootContextPresent() throws IOException { public void expressionWhenApplicationContextAttributeIsSetThenUsed() throws IOException {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", "USER")); 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(); DefaultWebSecurityExpressionHandler expected = new DefaultWebSecurityExpressionHandler();
WebApplicationContext dispatcher = mock(WebApplicationContext.class); WebApplicationContext context = mock(WebApplicationContext.class);
given(dispatcher.getBeansOfType(SecurityExpressionHandler.class)) given(context.getBeansOfType(SecurityExpressionHandler.class))
.willReturn(Collections.<String, SecurityExpressionHandler>singletonMap("wipe", expected)); .willReturn(Collections.<String, SecurityExpressionHandler>singletonMap("wipe", expected));
given(dispatcher.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]); given(context.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);
this.request.setAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", dispatcher); this.request.setAttribute(WebAttributes.APPLICATION_CONTEXT_ATTRIBUTE, context);
this.tag.setAccess("permitAll"); this.tag.setAccess("permitAll");
assertThat(this.tag.authorize()).isTrue(); 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 { private class AuthzTag extends AbstractAuthorizeTag {
@@ -37,6 +37,22 @@ public final class WebAttributes {
*/ */
public static final String ACCESS_DENIED_403 = "SPRING_SECURITY_403_EXCEPTION"; 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.
* <p>
* When set, this attribute is preferred over the root web application context. The
* value must be of type {@link org.springframework.context.ApplicationContext}.
*
* <p>
* 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. * Used to cache an authentication-failure exception in the session.
* *