Skip to content

Commit d2f24ae

Browse files
committed
Add SecurityContextRepository to all Authentication Filters
Closes gh-10949
2 parents 980e046 + 9db79aa commit d2f24ae

File tree

16 files changed

+363
-0
lines changed

16 files changed

+363
-0
lines changed

cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
4343
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
4444
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
45+
import org.springframework.security.web.context.NullSecurityContextRepository;
46+
import org.springframework.security.web.context.SecurityContextRepository;
4547
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4648
import org.springframework.security.web.util.matcher.RequestMatcher;
4749
import org.springframework.util.Assert;
@@ -205,6 +207,8 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
205207

206208
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
207209

210+
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
211+
208212
public CasAuthenticationFilter() {
209213
super("/login/cas");
210214
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
@@ -223,6 +227,7 @@ protected final void successfulAuthentication(HttpServletRequest request, HttpSe
223227
SecurityContext context = SecurityContextHolder.createEmptyContext();
224228
context.setAuthentication(authResult);
225229
SecurityContextHolder.setContext(context);
230+
this.securityContextRepository.saveContext(context, request, response);
226231
if (this.eventPublisher != null) {
227232
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
228233
}
@@ -274,6 +279,18 @@ protected boolean requiresAuthentication(HttpServletRequest request, HttpServlet
274279
return result;
275280
}
276281

282+
/**
283+
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
284+
* authentication success. The default action is not to save the
285+
* {@link SecurityContext}.
286+
* @param securityContextRepository the {@link SecurityContextRepository} to use.
287+
* Cannot be null.
288+
*/
289+
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
290+
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
291+
this.securityContextRepository = securityContextRepository;
292+
}
293+
277294
/**
278295
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
279296
* @param proxyFailureHandler

cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
2222
import org.junit.jupiter.api.AfterEach;
2323
import org.junit.jupiter.api.Test;
24+
import org.mockito.ArgumentCaptor;
2425

2526
import org.springframework.mock.web.MockHttpServletRequest;
2627
import org.springframework.mock.web.MockHttpServletResponse;
@@ -32,12 +33,15 @@
3233
import org.springframework.security.core.Authentication;
3334
import org.springframework.security.core.AuthenticationException;
3435
import org.springframework.security.core.authority.AuthorityUtils;
36+
import org.springframework.security.core.context.SecurityContext;
3537
import org.springframework.security.core.context.SecurityContextHolder;
3638
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
39+
import org.springframework.security.web.context.SecurityContextRepository;
3740

3841
import static org.assertj.core.api.Assertions.assertThat;
3942
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
4043
import static org.mockito.ArgumentMatchers.any;
44+
import static org.mockito.ArgumentMatchers.eq;
4145
import static org.mockito.BDDMockito.given;
4246
import static org.mockito.Mockito.mock;
4347
import static org.mockito.Mockito.verify;
@@ -182,6 +186,38 @@ public void testDoFilterAuthenticateAll() throws Exception {
182186
verify(successHandler).onAuthenticationSuccess(request, response, authentication);
183187
}
184188

189+
@Test
190+
public void testSecurityContextHolder() throws Exception {
191+
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
192+
AuthenticationManager manager = mock(AuthenticationManager.class);
193+
Authentication authentication = new TestingAuthenticationToken("un", "pwd", "ROLE_USER");
194+
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
195+
ServiceProperties serviceProperties = new ServiceProperties();
196+
serviceProperties.setAuthenticateAllArtifacts(true);
197+
MockHttpServletRequest request = new MockHttpServletRequest();
198+
request.setParameter("ticket", "ST-1-123");
199+
request.setServletPath("/authenticate");
200+
MockHttpServletResponse response = new MockHttpServletResponse();
201+
FilterChain chain = mock(FilterChain.class);
202+
CasAuthenticationFilter filter = new CasAuthenticationFilter();
203+
filter.setServiceProperties(serviceProperties);
204+
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
205+
filter.setAuthenticationManager(manager);
206+
filter.setSecurityContextRepository(securityContextRepository);
207+
filter.afterPropertiesSet();
208+
filter.doFilter(request, response, chain);
209+
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull()
210+
.withFailMessage("Authentication should not be null");
211+
verify(chain).doFilter(request, response);
212+
// validate for when the filterProcessUrl matches
213+
filter.setFilterProcessesUrl(request.getServletPath());
214+
SecurityContextHolder.clearContext();
215+
filter.doFilter(request, response, chain);
216+
ArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);
217+
verify(securityContextRepository).saveContext(contextArg.capture(), eq(request), eq(response));
218+
assertThat(contextArg.getValue().getAuthentication().getPrincipal()).isEqualTo(authentication.getName());
219+
}
220+
185221
// SEC-1592
186222
@Test
187223
public void testChainNotInvokedForProxyReceptor() throws Exception {

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
import org.springframework.security.web.AuthenticationEntryPoint;
3939
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
4040
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
41+
import org.springframework.security.web.context.NullSecurityContextRepository;
42+
import org.springframework.security.web.context.SecurityContextRepository;
4143
import org.springframework.util.Assert;
4244
import org.springframework.web.filter.OncePerRequestFilter;
4345

@@ -75,6 +77,8 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter
7577

7678
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
7779

80+
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
81+
7882
/**
7983
* Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
8084
* @param authenticationManagerResolver
@@ -131,6 +135,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
131135
SecurityContext context = SecurityContextHolder.createEmptyContext();
132136
context.setAuthentication(authenticationResult);
133137
SecurityContextHolder.setContext(context);
138+
this.securityContextRepository.saveContext(context, request, response);
134139
if (this.logger.isDebugEnabled()) {
135140
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authenticationResult));
136141
}
@@ -143,6 +148,18 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
143148
}
144149
}
145150

151+
/**
152+
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
153+
* authentication success. The default action is not to save the
154+
* {@link SecurityContext}.
155+
* @param securityContextRepository the {@link SecurityContextRepository} to use.
156+
* Cannot be null.
157+
*/
158+
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
159+
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
160+
this.securityContextRepository = securityContextRepository;
161+
}
162+
146163
/**
147164
* Set the {@link BearerTokenResolver} to use. Defaults to
148165
* {@link DefaultBearerTokenResolver}.

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,23 @@
3636
import org.springframework.security.authentication.AuthenticationManager;
3737
import org.springframework.security.authentication.AuthenticationManagerResolver;
3838
import org.springframework.security.authentication.AuthenticationServiceException;
39+
import org.springframework.security.authentication.TestingAuthenticationToken;
40+
import org.springframework.security.core.context.SecurityContext;
3941
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
4042
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
4143
import org.springframework.security.oauth2.server.resource.BearerTokenError;
4244
import org.springframework.security.oauth2.server.resource.BearerTokenErrorCodes;
4345
import org.springframework.security.web.AuthenticationEntryPoint;
4446
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
47+
import org.springframework.security.web.context.SecurityContextRepository;
4548

4649
import static org.assertj.core.api.Assertions.assertThat;
4750
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
4851
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
4952
import static org.mockito.ArgumentMatchers.any;
53+
import static org.mockito.ArgumentMatchers.eq;
5054
import static org.mockito.BDDMockito.given;
55+
import static org.mockito.Mockito.mock;
5156
import static org.mockito.Mockito.verify;
5257
import static org.mockito.Mockito.verifyNoMoreInteractions;
5358

@@ -102,6 +107,26 @@ public void doFilterWhenBearerTokenPresentThenAuthenticates() throws ServletExce
102107
assertThat(captor.getValue().getPrincipal()).isEqualTo("token");
103108
}
104109

110+
@Test
111+
public void doFilterWhenSecurityContextRepositoryThenSaves() throws ServletException, IOException {
112+
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
113+
String token = "token";
114+
given(this.bearerTokenResolver.resolve(this.request)).willReturn(token);
115+
TestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken("test", "password");
116+
given(this.authenticationManager.authenticate(any())).willReturn(expectedAuthentication);
117+
BearerTokenAuthenticationFilter filter = addMocks(
118+
new BearerTokenAuthenticationFilter(this.authenticationManager));
119+
filter.setSecurityContextRepository(securityContextRepository);
120+
filter.doFilter(this.request, this.response, this.filterChain);
121+
ArgumentCaptor<BearerTokenAuthenticationToken> captor = ArgumentCaptor
122+
.forClass(BearerTokenAuthenticationToken.class);
123+
verify(this.authenticationManager).authenticate(captor.capture());
124+
assertThat(captor.getValue().getPrincipal()).isEqualTo(token);
125+
ArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);
126+
verify(securityContextRepository).saveContext(contextArg.capture(), eq(this.request), eq(this.response));
127+
assertThat(contextArg.getValue().getAuthentication().getName()).isEqualTo(expectedAuthentication.getName());
128+
}
129+
105130
@Test
106131
public void doFilterWhenUsingAuthenticationManagerResolverThenAuthenticates() throws Exception {
107132
BearerTokenAuthenticationFilter filter = addMocks(

web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import org.springframework.security.core.context.SecurityContextHolder;
4343
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
4444
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
45+
import org.springframework.security.web.context.NullSecurityContextRepository;
46+
import org.springframework.security.web.context.SecurityContextRepository;
4547
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4648
import org.springframework.security.web.util.matcher.RequestMatcher;
4749
import org.springframework.util.Assert;
@@ -134,6 +136,8 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
134136

135137
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
136138

139+
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
140+
137141
/**
138142
* @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.
139143
*/
@@ -314,6 +318,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
314318
SecurityContext context = SecurityContextHolder.createEmptyContext();
315319
context.setAuthentication(authResult);
316320
SecurityContextHolder.setContext(context);
321+
this.securityContextRepository.saveContext(context, request, response);
317322
if (this.logger.isDebugEnabled()) {
318323
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
319324
}
@@ -435,6 +440,18 @@ public void setAuthenticationFailureHandler(AuthenticationFailureHandler failure
435440
this.failureHandler = failureHandler;
436441
}
437442

443+
/**
444+
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
445+
* authentication success. The default action is not to save the
446+
* {@link SecurityContext}.
447+
* @param securityContextRepository the {@link SecurityContextRepository} to use.
448+
* Cannot be null.
449+
*/
450+
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
451+
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
452+
this.securityContextRepository = securityContextRepository;
453+
}
454+
438455
protected AuthenticationSuccessHandler getSuccessHandler() {
439456
return this.successHandler;
440457
}

web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import org.springframework.security.core.AuthenticationException;
3333
import org.springframework.security.core.context.SecurityContext;
3434
import org.springframework.security.core.context.SecurityContextHolder;
35+
import org.springframework.security.web.context.NullSecurityContextRepository;
36+
import org.springframework.security.web.context.SecurityContextRepository;
3537
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
3638
import org.springframework.security.web.util.matcher.RequestMatcher;
3739
import org.springframework.util.Assert;
@@ -74,6 +76,8 @@ public class AuthenticationFilter extends OncePerRequestFilter {
7476
private AuthenticationFailureHandler failureHandler = new AuthenticationEntryPointFailureHandler(
7577
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
7678

79+
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
80+
7781
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
7882

7983
public AuthenticationFilter(AuthenticationManager authenticationManager,
@@ -135,6 +139,18 @@ public void setAuthenticationManagerResolver(
135139
this.authenticationManagerResolver = authenticationManagerResolver;
136140
}
137141

142+
/**
143+
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
144+
* authentication success. The default action is not to save the
145+
* {@link SecurityContext}.
146+
* @param securityContextRepository the {@link SecurityContextRepository} to use.
147+
* Cannot be null.
148+
*/
149+
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
150+
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
151+
this.securityContextRepository = securityContextRepository;
152+
}
153+
138154
@Override
139155
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
140156
throws ServletException, IOException {
@@ -173,6 +189,7 @@ private void successfulAuthentication(HttpServletRequest request, HttpServletRes
173189
SecurityContext context = SecurityContextHolder.createEmptyContext();
174190
context.setAuthentication(authentication);
175191
SecurityContextHolder.setContext(context);
192+
this.securityContextRepository.saveContext(context, request, response);
176193
this.successHandler.onAuthenticationSuccess(request, response, chain, authentication);
177194
}
178195

web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
4141
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
4242
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
43+
import org.springframework.security.web.context.NullSecurityContextRepository;
44+
import org.springframework.security.web.context.SecurityContextRepository;
4345
import org.springframework.security.web.util.matcher.RequestMatcher;
4446
import org.springframework.util.Assert;
4547
import org.springframework.web.filter.GenericFilterBean;
@@ -104,6 +106,8 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
104106

105107
private RequestMatcher requiresAuthenticationRequestMatcher = new PreAuthenticatedProcessingRequestMatcher();
106108

109+
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
110+
107111
/**
108112
* Check whether all required properties have been set.
109113
*/
@@ -210,6 +214,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
210214
SecurityContext context = SecurityContextHolder.createEmptyContext();
211215
context.setAuthentication(authResult);
212216
SecurityContextHolder.setContext(context);
217+
this.securityContextRepository.saveContext(context, request, response);
213218
if (this.eventPublisher != null) {
214219
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
215220
}
@@ -242,6 +247,18 @@ public void setApplicationEventPublisher(ApplicationEventPublisher anApplication
242247
this.eventPublisher = anApplicationEventPublisher;
243248
}
244249

250+
/**
251+
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
252+
* authentication success. The default action is not to save the
253+
* {@link SecurityContext}.
254+
* @param securityContextRepository the {@link SecurityContextRepository} to use.
255+
* Cannot be null.
256+
*/
257+
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
258+
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
259+
this.securityContextRepository = securityContextRepository;
260+
}
261+
245262
/**
246263
* @param authenticationDetailsSource The AuthenticationDetailsSource to use
247264
*/

web/src/main/java/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationFilter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.springframework.security.core.context.SecurityContextHolder;
3737
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
3838
import org.springframework.security.web.authentication.RememberMeServices;
39+
import org.springframework.security.web.context.NullSecurityContextRepository;
40+
import org.springframework.security.web.context.SecurityContextRepository;
3941
import org.springframework.util.Assert;
4042
import org.springframework.web.filter.GenericFilterBean;
4143

@@ -73,6 +75,8 @@ public class RememberMeAuthenticationFilter extends GenericFilterBean implements
7375

7476
private RememberMeServices rememberMeServices;
7577

78+
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
79+
7680
public RememberMeAuthenticationFilter(AuthenticationManager authenticationManager,
7781
RememberMeServices rememberMeServices) {
7882
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
@@ -114,6 +118,7 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,
114118
onSuccessfulAuthentication(request, response, rememberMeAuth);
115119
this.logger.debug(LogMessage.of(() -> "SecurityContextHolder populated with remember-me token: '"
116120
+ SecurityContextHolder.getContext().getAuthentication() + "'"));
121+
this.securityContextRepository.saveContext(context, request, response);
117122
if (this.eventPublisher != null) {
118123
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
119124
SecurityContextHolder.getContext().getAuthentication(), this.getClass()));
@@ -179,4 +184,16 @@ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler success
179184
this.successHandler = successHandler;
180185
}
181186

187+
/**
188+
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
189+
* authentication success. The default action is not to save the
190+
* {@link SecurityContext}.
191+
* @param securityContextRepository the {@link SecurityContextRepository} to use.
192+
* Cannot be null.
193+
*/
194+
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
195+
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
196+
this.securityContextRepository = securityContextRepository;
197+
}
198+
182199
}

0 commit comments

Comments
 (0)