Skip to content

Commit 94f51d0

Browse files
committed
Use SecurityContextHolderStrategy for Taglibs
Issue gh-11060
1 parent 518bc75 commit 94f51d0

File tree

6 files changed

+134
-11
lines changed

6 files changed

+134
-11
lines changed

taglibs/src/main/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTag.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2004-2010 the original author or authors.
2+
* Copyright 2004-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,7 +32,9 @@
3232
import org.springframework.security.access.expression.ExpressionUtils;
3333
import org.springframework.security.access.expression.SecurityExpressionHandler;
3434
import org.springframework.security.core.Authentication;
35+
import org.springframework.security.core.context.SecurityContext;
3536
import org.springframework.security.core.context.SecurityContextHolder;
37+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3638
import org.springframework.security.web.FilterInvocation;
3739
import org.springframework.security.web.WebAttributes;
3840
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
@@ -110,7 +112,7 @@ public boolean authorize() throws IOException {
110112
* @throws IOException
111113
*/
112114
public boolean authorizeUsingAccessExpression() throws IOException {
113-
if (SecurityContextHolder.getContext().getAuthentication() == null) {
115+
if (getContext().getAuthentication() == null) {
114116
return false;
115117
}
116118
SecurityExpressionHandler<FilterInvocation> handler = getExpressionHandler();
@@ -131,7 +133,7 @@ protected EvaluationContext createExpressionEvaluationContext(SecurityExpression
131133
FilterInvocation f = new FilterInvocation(getRequest(), getResponse(), (request, response) -> {
132134
throw new UnsupportedOperationException();
133135
});
134-
return handler.createEvaluationContext(SecurityContextHolder.getContext().getAuthentication(), f);
136+
return handler.createEvaluationContext(getContext().getAuthentication(), f);
135137
}
136138

137139
/**
@@ -142,7 +144,7 @@ protected EvaluationContext createExpressionEvaluationContext(SecurityExpression
142144
*/
143145
public boolean authorizeUsingUrlCheck() throws IOException {
144146
String contextPath = ((HttpServletRequest) getRequest()).getContextPath();
145-
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
147+
Authentication currentUser = getContext().getAuthentication();
146148
return getPrivilegeEvaluator().isAllowed(contextPath, getUrl(), getMethod(), currentUser);
147149
}
148150

@@ -170,6 +172,17 @@ public void setMethod(String method) {
170172
this.method = (method != null) ? method.toUpperCase() : null;
171173
}
172174

175+
private SecurityContext getContext() {
176+
ApplicationContext appContext = SecurityWebApplicationContextUtils
177+
.findRequiredWebApplicationContext(getServletContext());
178+
String[] names = appContext.getBeanNamesForType(SecurityContextHolderStrategy.class);
179+
if (names.length == 1) {
180+
SecurityContextHolderStrategy strategy = appContext.getBean(SecurityContextHolderStrategy.class);
181+
return strategy.getContext();
182+
}
183+
return SecurityContextHolder.getContext();
184+
}
185+
173186
@SuppressWarnings({ "unchecked", "rawtypes" })
174187
private SecurityExpressionHandler<FilterInvocation> getExpressionHandler() throws IOException {
175188
ApplicationContext appContext = SecurityWebApplicationContextUtils

taglibs/src/main/java/org/springframework/security/taglibs/authz/AccessControlListTag.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.security.access.PermissionEvaluator;
3333
import org.springframework.security.core.Authentication;
3434
import org.springframework.security.core.context.SecurityContextHolder;
35+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3536
import org.springframework.security.taglibs.TagLibConfig;
3637
import org.springframework.security.web.context.support.SecurityWebApplicationContextUtils;
3738

@@ -56,6 +57,9 @@ public class AccessControlListTag extends TagSupport {
5657

5758
protected static final Log logger = LogFactory.getLog(AccessControlListTag.class);
5859

60+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
61+
.getContextHolderStrategy();
62+
5963
private ApplicationContext applicationContext;
6064

6165
private Object domainObject;
@@ -77,7 +81,7 @@ public int doStartTag() throws JspException {
7781
// Of course they have access to a null object!
7882
return evalBody();
7983
}
80-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
84+
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
8185
if (authentication == null) {
8286
logger.debug("SecurityContextHolder did not return a non-null Authentication object, so skipping tag body");
8387
return skipBody();
@@ -145,6 +149,12 @@ private void initializeIfRequired() throws JspException {
145149
}
146150
this.applicationContext = getContext(this.pageContext);
147151
this.permissionEvaluator = getBeanOfType(PermissionEvaluator.class);
152+
String[] names = this.applicationContext.getBeanNamesForType(SecurityContextHolderStrategy.class);
153+
if (names.length == 1) {
154+
SecurityContextHolderStrategy strategy = this.applicationContext
155+
.getBean(SecurityContextHolderStrategy.class);
156+
this.securityContextHolderStrategy = strategy;
157+
}
148158
}
149159

150160
private <T> T getBeanOfType(Class<T> type) throws JspException {

taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,20 @@
1818

1919
import java.io.IOException;
2020

21+
import jakarta.servlet.ServletContext;
2122
import jakarta.servlet.jsp.JspException;
2223
import jakarta.servlet.jsp.PageContext;
2324
import jakarta.servlet.jsp.tagext.Tag;
2425
import jakarta.servlet.jsp.tagext.TagSupport;
2526

2627
import org.springframework.beans.BeanWrapperImpl;
2728
import org.springframework.beans.BeansException;
29+
import org.springframework.context.ApplicationContext;
2830
import org.springframework.security.core.Authentication;
2931
import org.springframework.security.core.context.SecurityContext;
3032
import org.springframework.security.core.context.SecurityContextHolder;
33+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
34+
import org.springframework.security.web.context.support.SecurityWebApplicationContextUtils;
3135
import org.springframework.security.web.util.TextEscapeUtils;
3236
import org.springframework.web.util.TagUtils;
3337

@@ -42,6 +46,9 @@
4246
*/
4347
public class AuthenticationTag extends TagSupport {
4448

49+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
50+
.getContextHolderStrategy();
51+
4552
private String var;
4653

4754
private String property;
@@ -76,6 +83,18 @@ public void setScope(String scope) {
7683
this.scopeSpecified = true;
7784
}
7885

86+
public void setPageContext(PageContext pageContext) {
87+
super.setPageContext(pageContext);
88+
ServletContext servletContext = pageContext.getServletContext();
89+
ApplicationContext context = SecurityWebApplicationContextUtils
90+
.findRequiredWebApplicationContext(servletContext);
91+
String[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);
92+
if (names.length == 1) {
93+
SecurityContextHolderStrategy strategy = context.getBean(SecurityContextHolderStrategy.class);
94+
this.securityContextHolderStrategy = strategy;
95+
}
96+
}
97+
7998
@Override
8099
public int doStartTag() throws JspException {
81100
return super.doStartTag();
@@ -86,12 +105,11 @@ public int doEndTag() throws JspException {
86105
Object result = null;
87106
// determine the value by...
88107
if (this.property != null) {
89-
if ((SecurityContextHolder.getContext() == null)
90-
|| !(SecurityContextHolder.getContext() instanceof SecurityContext)
91-
|| (SecurityContextHolder.getContext().getAuthentication() == null)) {
108+
SecurityContext context = this.securityContextHolderStrategy.getContext();
109+
if ((context == null) || !(context instanceof SecurityContext) || (context.getAuthentication() == null)) {
92110
return Tag.EVAL_PAGE;
93111
}
94-
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
112+
Authentication auth = context.getAuthentication();
95113
if (auth.getPrincipal() == null) {
96114
return Tag.EVAL_PAGE;
97115
}

taglibs/src/test/java/org/springframework/security/taglibs/authz/AbstractAuthorizeTagTests.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,11 +31,15 @@
3131
import org.springframework.mock.web.MockServletContext;
3232
import org.springframework.security.access.expression.SecurityExpressionHandler;
3333
import org.springframework.security.authentication.TestingAuthenticationToken;
34+
import org.springframework.security.core.authority.AuthorityUtils;
3435
import org.springframework.security.core.context.SecurityContextHolder;
36+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
37+
import org.springframework.security.core.context.SecurityContextImpl;
3538
import org.springframework.security.web.WebAttributes;
3639
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
3740
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
3841
import org.springframework.web.context.WebApplicationContext;
42+
import org.springframework.web.context.support.GenericWebApplicationContext;
3943

4044
import static org.assertj.core.api.Assertions.assertThat;
4145
import static org.mockito.ArgumentMatchers.any;
@@ -73,12 +77,33 @@ public void teardown() {
7377

7478
@Test
7579
public void privilegeEvaluatorFromRequest() throws IOException {
80+
WebApplicationContext wac = mock(WebApplicationContext.class);
81+
this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
82+
given(wac.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);
83+
String uri = "/something";
84+
WebInvocationPrivilegeEvaluator expected = mock(WebInvocationPrivilegeEvaluator.class);
85+
this.tag.setUrl(uri);
86+
this.request.setAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE, expected);
87+
this.tag.authorizeUsingUrlCheck();
88+
verify(expected).isAllowed(eq(""), eq(uri), eq("GET"), any());
89+
}
90+
91+
@Test
92+
public void privilegeEvaluatorFromRequestUsesSecurityContextHolderStrategy() throws IOException {
93+
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
94+
given(strategy.getContext()).willReturn(new SecurityContextImpl(
95+
new TestingAuthenticationToken("user", "password", AuthorityUtils.NO_AUTHORITIES)));
96+
GenericWebApplicationContext wac = new GenericWebApplicationContext();
97+
wac.registerBean(SecurityContextHolderStrategy.class, () -> strategy);
98+
wac.refresh();
99+
this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
76100
String uri = "/something";
77101
WebInvocationPrivilegeEvaluator expected = mock(WebInvocationPrivilegeEvaluator.class);
78102
this.tag.setUrl(uri);
79103
this.request.setAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE, expected);
80104
this.tag.authorizeUsingUrlCheck();
81105
verify(expected).isAllowed(eq(""), eq(uri), eq("GET"), any());
106+
verify(strategy).getContext();
82107
}
83108

84109
@Test
@@ -89,6 +114,7 @@ public void privilegeEvaluatorFromChildContext() throws IOException {
89114
WebApplicationContext wac = mock(WebApplicationContext.class);
90115
given(wac.getBeansOfType(WebInvocationPrivilegeEvaluator.class))
91116
.willReturn(Collections.singletonMap("wipe", expected));
117+
given(wac.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);
92118
this.servletContext.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac);
93119
this.tag.authorizeUsingUrlCheck();
94120
verify(expected).isAllowed(eq(""), eq(uri), eq("GET"), any());
@@ -103,6 +129,7 @@ public void expressionFromChildContext() throws IOException {
103129
WebApplicationContext wac = mock(WebApplicationContext.class);
104130
given(wac.getBeansOfType(SecurityExpressionHandler.class))
105131
.willReturn(Collections.<String, SecurityExpressionHandler>singletonMap("wipe", expected));
132+
given(wac.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);
106133
this.servletContext.setAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher", wac);
107134
assertThat(this.tag.authorize()).isTrue();
108135
}

taglibs/src/test/java/org/springframework/security/taglibs/authz/AccessControlListTagTests.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,7 +33,10 @@
3333
import org.springframework.security.authentication.TestingAuthenticationToken;
3434
import org.springframework.security.core.Authentication;
3535
import org.springframework.security.core.context.SecurityContextHolder;
36+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
37+
import org.springframework.security.core.context.SecurityContextImpl;
3638
import org.springframework.web.context.WebApplicationContext;
39+
import org.springframework.web.context.support.GenericWebApplicationContext;
3740

3841
import static org.assertj.core.api.Assertions.assertThat;
3942
import static org.mockito.BDDMockito.given;
@@ -67,6 +70,7 @@ public void setup() {
6770
Map beanMap = new HashMap();
6871
beanMap.put("pe", this.pe);
6972
given(ctx.getBeansOfType(PermissionEvaluator.class)).willReturn(beanMap);
73+
given(ctx.getBeanNamesForType(SecurityContextHolderStrategy.class)).willReturn(new String[0]);
7074
MockServletContext servletCtx = new MockServletContext();
7175
servletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);
7276
this.pageContext = new MockPageContext(servletCtx, new MockHttpServletRequest(), new MockHttpServletResponse());
@@ -91,6 +95,30 @@ public void bodyIsEvaluatedIfAclGrantsAccess() throws Exception {
9195
assertThat((Boolean) this.pageContext.getAttribute("allowed")).isTrue();
9296
}
9397

98+
@Test
99+
public void securityContextHolderStrategyIsUsedIfConfigured() throws Exception {
100+
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
101+
given(strategy.getContext()).willReturn(new SecurityContextImpl(this.bob));
102+
GenericWebApplicationContext context = new GenericWebApplicationContext();
103+
context.registerBean(SecurityContextHolderStrategy.class, () -> strategy);
104+
context.registerBean(PermissionEvaluator.class, () -> this.pe);
105+
context.refresh();
106+
MockServletContext servletCtx = new MockServletContext();
107+
servletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
108+
this.pageContext = new MockPageContext(servletCtx, new MockHttpServletRequest(), new MockHttpServletResponse());
109+
this.tag.setPageContext(this.pageContext);
110+
Object domainObject = new Object();
111+
given(this.pe.hasPermission(this.bob, domainObject, "READ")).willReturn(true);
112+
this.tag.setDomainObject(domainObject);
113+
this.tag.setHasPermission("READ");
114+
this.tag.setVar("allowed");
115+
assertThat(this.tag.getDomainObject()).isSameAs(domainObject);
116+
assertThat(this.tag.getHasPermission()).isEqualTo("READ");
117+
assertThat(this.tag.doStartTag()).isEqualTo(Tag.EVAL_BODY_INCLUDE);
118+
assertThat((Boolean) this.pageContext.getAttribute("allowed")).isTrue();
119+
verify(strategy).getContext();
120+
}
121+
94122
@Test
95123
public void childContext() throws Exception {
96124
ServletContext servletContext = this.pageContext.getServletContext();

taglibs/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,23 @@
2121
import org.junit.jupiter.api.AfterEach;
2222
import org.junit.jupiter.api.Test;
2323

24+
import org.springframework.mock.web.MockPageContext;
25+
import org.springframework.mock.web.MockServletContext;
2426
import org.springframework.security.authentication.TestingAuthenticationToken;
2527
import org.springframework.security.core.Authentication;
2628
import org.springframework.security.core.authority.AuthorityUtils;
2729
import org.springframework.security.core.context.SecurityContextHolder;
30+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
31+
import org.springframework.security.core.context.SecurityContextImpl;
2832
import org.springframework.security.core.userdetails.User;
33+
import org.springframework.web.context.WebApplicationContext;
34+
import org.springframework.web.context.support.GenericWebApplicationContext;
2935

3036
import static org.assertj.core.api.Assertions.assertThat;
3137
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
38+
import static org.mockito.BDDMockito.given;
39+
import static org.mockito.Mockito.mock;
40+
import static org.mockito.Mockito.verify;
3241

3342
/**
3443
* Tests {@link AuthenticationTag}.
@@ -130,6 +139,24 @@ public void settingHtmlEscapeToFalsePreventsEscaping() throws Exception {
130139
assertThat(this.authenticationTag.getLastMessage()).isEqualTo("<>& ");
131140
}
132141

142+
@Test
143+
public void setSecurityContextHolderStrategyThenUses() throws Exception {
144+
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
145+
given(strategy.getContext()).willReturn(new SecurityContextImpl(
146+
new TestingAuthenticationToken("rodAsString", "koala", AuthorityUtils.NO_AUTHORITIES)));
147+
MockServletContext servletContext = new MockServletContext();
148+
GenericWebApplicationContext applicationContext = new GenericWebApplicationContext();
149+
applicationContext.registerBean(SecurityContextHolderStrategy.class, () -> strategy);
150+
applicationContext.refresh();
151+
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
152+
this.authenticationTag.setPageContext(new MockPageContext(servletContext));
153+
this.authenticationTag.setProperty("principal");
154+
assertThat(this.authenticationTag.doStartTag()).isEqualTo(Tag.SKIP_BODY);
155+
assertThat(this.authenticationTag.doEndTag()).isEqualTo(Tag.EVAL_PAGE);
156+
assertThat(this.authenticationTag.getLastMessage()).isEqualTo("rodAsString");
157+
verify(strategy).getContext();
158+
}
159+
133160
private class MyAuthenticationTag extends AuthenticationTag {
134161

135162
String lastMessage = null;

0 commit comments

Comments
 (0)