Add SecurityContextHolderStrategy Java Configuration for Defaults

Issue gh-11061
This commit is contained in:
Josh Cummings 2022-05-26 14:19:06 -06:00
parent 31e25b115e
commit 2c09a300b6
17 changed files with 269 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,6 +24,8 @@ import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.BeanResolver;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver;
import org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver;
@ -50,11 +52,15 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
private BeanResolver beanResolver;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
@Override
@SuppressWarnings("deprecation")
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
authenticationPrincipalResolver.setBeanResolver(this.beanResolver);
authenticationPrincipalResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
argumentResolvers.add(authenticationPrincipalResolver);
argumentResolvers
.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
@ -72,6 +78,9 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());
if (applicationContext.getBeanNamesForType(SecurityContextHolderStrategy.class).length == 1) {
this.securityContextHolderStrategy = applicationContext.getBean(SecurityContextHolderStrategy.class);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -299,6 +299,7 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
.getSecurityContextRepository();
this.authFilter.setSecurityContextRepository(securityContextRepository);
}
this.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
F filter = postProcess(this.authFilter);
http.addFilter(filter);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,14 @@
package org.springframework.security.config.annotation.web.configurers;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.DefaultSecurityFilterChain;
/**
@ -32,6 +35,8 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
private SecurityContextHolderStrategy securityContextHolderStrategy;
/**
* Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
* version of the configuration can be applied.
@ -49,4 +54,19 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
return (T) this;
}
protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {
if (this.securityContextHolderStrategy != null) {
return this.securityContextHolderStrategy;
}
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
String[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);
if (names.length == 1) {
this.securityContextHolderStrategy = context.getBean(SecurityContextHolderStrategy.class);
}
else {
this.securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
}
return this.securityContextHolderStrategy;
}
}

View File

@ -146,6 +146,7 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
}
if (this.authenticationFilter == null) {
this.authenticationFilter = new AnonymousAuthenticationFilter(getKey(), this.principal, this.authorities);
this.authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
}
this.authenticationProvider = postProcess(this.authenticationProvider);
http.authenticationProvider(this.authenticationProvider);

View File

@ -86,6 +86,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
authorizationFilter.setAuthorizationEventPublisher(this.publisher);
authorizationFilter.setShouldFilterAllDispatcherTypes(this.registry.shouldFilterAllDispatcherTypes);
authorizationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
http.addFilter(postProcess(authorizationFilter));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -187,6 +187,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
getRequestCache(http));
AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
exceptionTranslationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
http.addFilter(exceptionTranslationFilter);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -199,6 +199,7 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
if (rememberMeServices != null) {
basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
}
basicAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
http.addFilter(basicAuthenticationFilter);
}

View File

@ -329,6 +329,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
this.logoutHandlers.add(postProcess(new LogoutSuccessEventPublishingLogoutHandler()));
LogoutHandler[] handlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
LogoutFilter result = new LogoutFilter(getLogoutSuccessHandler(), handlers);
result.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
result.setLogoutRequestMatcher(getLogoutRequestMatcher(http));
result = postProcess(result);
return result;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -108,11 +108,13 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
if (this.requireExplicitSave) {
SecurityContextHolderFilter securityContextHolderFilter = postProcess(
new SecurityContextHolderFilter(securityContextRepository));
securityContextHolderFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
http.addFilter(securityContextHolderFilter);
}
else {
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
securityContextRepository);
securityContextFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
? sessionManagement.getSessionCreationPolicy() : null;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -370,6 +370,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
if (trustResolver != null) {
sessionManagementFilter.setTrustResolver(trustResolver);
}
sessionManagementFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
sessionManagementFilter = postProcess(sessionManagementFilter);
http.addFilter(sessionManagementFilter);
if (isConcurrentSessionControlEnabled()) {
@ -500,7 +501,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);
RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
sessionRegistry);
registerSessionStrategy = postProcess(registerSessionStrategy);

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl;
public class MockSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private SecurityContext context;
@Override
public void clearContext() {
this.context = null;
}
@Override
public SecurityContext getContext() {
if (this.context == null) {
this.context = createEmptyContext();
}
return this.context;
}
@Override
public void setContext(SecurityContext context) {
this.context = context;
}
@Override
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import org.mockito.ArgumentMatcher;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextChangedEvent;
import static org.mockito.ArgumentMatchers.argThat;
public final class SecurityContextChangedListenerArgumentMatchers {
public static SecurityContextChangedEvent setAuthentication(Class<? extends Authentication> authenticationClass) {
return argThat(new ArgumentMatcher<SecurityContextChangedEvent>() {
public boolean matches(SecurityContextChangedEvent event) {
Authentication previous = authentication(event.getOldContext());
Authentication next = authentication(event.getNewContext());
return previous == null && next != null && authenticationClass.isAssignableFrom(next.getClass());
}
public String toString() {
return "authentication set to " + authenticationClass;
}
});
}
private static Authentication authentication(SecurityContext context) {
if (context == null) {
return null;
}
return context.getAuthentication();
}
private SecurityContextChangedListenerArgumentMatchers() {
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.MockSecurityContextHolderStrategy;
import org.springframework.security.core.context.ListeningSecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@Configuration
public class SecurityContextChangedListenerConfig {
private SecurityContextHolderStrategy strategy = new MockSecurityContextHolderStrategy();
private SecurityContextChangedListener listener = mock(SecurityContextChangedListener.class);
@Bean
SecurityContextHolderStrategy securityContextHolderStrategy() {
return spy(new ListeningSecurityContextHolderStrategy(this.strategy, this.listener));
}
@Bean
SecurityContextChangedListener securityContextChangedListener() {
return this.listener;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,8 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -27,13 +29,16 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.mockito.Mockito.verify;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -62,6 +67,16 @@ public class AnonymousConfigurerTests {
this.mockMvc.perform(get("/")).andExpect(content().string("principal"));
}
@Test
public void requestWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
this.spring.register(AnonymousPrincipalInLambdaConfig.class, SecurityContextChangedListenerConfig.class,
PrincipalController.class).autowire();
this.mockMvc.perform(get("/")).andExpect(content().string("principal"));
SecurityContextChangedListener listener = this.spring.getContext()
.getBean(SecurityContextChangedListener.class);
verify(listener).securityContextChanged(setAuthentication(AnonymousAuthenticationToken.class));
}
@Test
public void requestWhenAnonymousDisabledInLambdaThenRespondsWithForbidden() throws Exception {
this.spring.register(AnonymousDisabledInLambdaConfig.class, PrincipalController.class).autowire();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -34,6 +36,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
@ -47,6 +51,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -192,6 +197,17 @@ public class ExceptionHandlingConfigurerTests {
.resolveMediaTypes(any(NativeWebRequest.class));
}
@Test
public void getWhenCustomSecurityContextHolderStrategyThenUsed() throws Exception {
this.spring.register(SecurityContextChangedListenerConfig.class, DefaultSecurityConfig.class).autowire();
this.mvc.perform(get("/"));
SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);
verify(strategy, atLeastOnce()).getContext();
SecurityContextChangedListener listener = this.spring.getContext()
.getBean(SecurityContextChangedListener.class);
verify(listener).securityContextChanged(setAuthentication(AnonymousAuthenticationToken.class));
}
@Test
public void getWhenUsingDefaultsAndUnauthenticatedThenRedirectsToLogin() throws Exception {
this.spring.register(DefaultHttpConfig.class).autowire();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
@ -30,6 +32,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.users.AuthenticationTestConfiguration;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
import org.springframework.security.web.PortMapper;
@ -42,10 +46,12 @@ import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.logout;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
@ -97,6 +103,24 @@ public class FormLoginConfigurerTests {
// @formatter:on
}
@Test
public void formLoginWhenSecurityContextHolderStrategyThenUses() throws Exception {
this.spring.register(FormLoginConfig.class, SecurityContextChangedListenerConfig.class).autowire();
// @formatter:off
SecurityMockMvcRequestBuilders.FormLoginRequestBuilder loginRequest = formLogin()
.user("username", "user")
.password("password", "password");
this.mockMvc.perform(loginRequest)
.andExpect(status().isFound())
.andExpect(redirectedUrl("/"));
// @formatter:on
SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class);
verify(strategy, atLeastOnce()).getContext();
SecurityContextChangedListener listener = this.spring.getContext()
.getBean(SecurityContextChangedListener.class);
verify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class));
}
@Test
public void loginWhenFormLoginConfiguredThenHasDefaultFailureUrl() throws Exception {
this.spring.register(FormLoginConfig.class).autowire();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,8 +25,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -35,6 +37,7 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
@ -53,6 +56,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@ -133,6 +137,17 @@ public class HttpBasicConfigurerTests {
.andExpect(content().string("user"));
}
@Test
public void httpBasicWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
this.spring.register(HttpBasic.class, Users.class, Home.class, SecurityContextChangedListenerConfig.class)
.autowire();
this.mvc.perform(get("/").with(httpBasic("user", "password"))).andExpect(status().isOk())
.andExpect(content().string("user"));
SecurityContextChangedListener listener = this.spring.getContext()
.getBean(SecurityContextChangedListener.class);
verify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class));
}
@EnableWebSecurity
static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {