Add SecurityContextHolderStrategy to Pre-authenticated scenarios
Issue gh-11060 Issue gh-11061
This commit is contained in:
@@ -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.
|
||||
@@ -198,7 +198,8 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends Abstr
|
||||
|
||||
@Override
|
||||
public void configure(H http) {
|
||||
J2eePreAuthenticatedProcessingFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class));
|
||||
J2eePreAuthenticatedProcessingFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class),
|
||||
http);
|
||||
http.addFilter(filter);
|
||||
}
|
||||
|
||||
@@ -208,12 +209,14 @@ public final class JeeConfigurer<H extends HttpSecurityBuilder<H>> extends Abstr
|
||||
* @param authenticationManager the {@link AuthenticationManager} to use.
|
||||
* @return the {@link J2eePreAuthenticatedProcessingFilter} to use.
|
||||
*/
|
||||
private J2eePreAuthenticatedProcessingFilter getFilter(AuthenticationManager authenticationManager) {
|
||||
private J2eePreAuthenticatedProcessingFilter getFilter(AuthenticationManager authenticationManager, H http) {
|
||||
if (this.j2eePreAuthenticatedProcessingFilter == null) {
|
||||
this.j2eePreAuthenticatedProcessingFilter = new J2eePreAuthenticatedProcessingFilter();
|
||||
this.j2eePreAuthenticatedProcessingFilter.setAuthenticationManager(authenticationManager);
|
||||
this.j2eePreAuthenticatedProcessingFilter
|
||||
.setAuthenticationDetailsSource(createWebAuthenticationDetailsSource());
|
||||
this.j2eePreAuthenticatedProcessingFilter
|
||||
.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||
this.j2eePreAuthenticatedProcessingFilter = postProcess(this.j2eePreAuthenticatedProcessingFilter);
|
||||
}
|
||||
|
||||
|
||||
@@ -183,11 +183,11 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
@Override
|
||||
public void configure(H http) {
|
||||
X509AuthenticationFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class));
|
||||
X509AuthenticationFilter filter = getFilter(http.getSharedObject(AuthenticationManager.class), http);
|
||||
http.addFilter(filter);
|
||||
}
|
||||
|
||||
private X509AuthenticationFilter getFilter(AuthenticationManager authenticationManager) {
|
||||
private X509AuthenticationFilter getFilter(AuthenticationManager authenticationManager, H http) {
|
||||
if (this.x509AuthenticationFilter == null) {
|
||||
this.x509AuthenticationFilter = new X509AuthenticationFilter();
|
||||
this.x509AuthenticationFilter.setAuthenticationManager(authenticationManager);
|
||||
@@ -197,6 +197,7 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
|
||||
if (this.authenticationDetailsSource != null) {
|
||||
this.x509AuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
|
||||
}
|
||||
this.x509AuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
|
||||
this.x509AuthenticationFilter = postProcess(this.x509AuthenticationFilter);
|
||||
}
|
||||
|
||||
|
||||
@@ -260,8 +260,8 @@ final class AuthenticationConfigBuilder {
|
||||
createOpenIDLoginFilter(sessionStrategy, authenticationManager,
|
||||
authenticationFilterSecurityContextRepositoryRef);
|
||||
createSaml2LoginFilter(authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||
createX509Filter(authenticationManager);
|
||||
createJeeFilter(authenticationManager);
|
||||
createX509Filter(authenticationManager, authenticationFilterSecurityContextHolderStrategyRef);
|
||||
createJeeFilter(authenticationManager, authenticationFilterSecurityContextHolderStrategyRef);
|
||||
createLogoutFilter(authenticationFilterSecurityContextHolderStrategyRef);
|
||||
createSaml2LogoutFilter(authenticationFilterSecurityContextHolderStrategyRef);
|
||||
createLoginPageFilterIfNeeded();
|
||||
@@ -625,7 +625,8 @@ final class AuthenticationConfigBuilder {
|
||||
this.bearerTokenAuthenticationFilter = resourceServerBuilder.parse(resourceServerElt, this.pc);
|
||||
}
|
||||
|
||||
void createX509Filter(BeanReference authManager) {
|
||||
void createX509Filter(BeanReference authManager,
|
||||
BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {
|
||||
Element x509Elt = DomUtils.getChildElementByTagName(this.httpElt, Elements.X509);
|
||||
RootBeanDefinition filter = null;
|
||||
if (x509Elt != null) {
|
||||
@@ -633,6 +634,8 @@ final class AuthenticationConfigBuilder {
|
||||
.rootBeanDefinition(X509AuthenticationFilter.class);
|
||||
filterBuilder.getRawBeanDefinition().setSource(this.pc.extractSource(x509Elt));
|
||||
filterBuilder.addPropertyValue("authenticationManager", authManager);
|
||||
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
||||
authenticationFilterSecurityContextHolderStrategyRef);
|
||||
String regex = x509Elt.getAttribute("subject-principal-regex");
|
||||
if (StringUtils.hasText(regex)) {
|
||||
BeanDefinitionBuilder extractor = BeanDefinitionBuilder
|
||||
@@ -673,7 +676,8 @@ final class AuthenticationConfigBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
void createJeeFilter(BeanReference authManager) {
|
||||
void createJeeFilter(BeanReference authManager,
|
||||
BeanMetadataElement authenticationFilterSecurityContextHolderStrategyRef) {
|
||||
Element jeeElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.JEE);
|
||||
RootBeanDefinition filter = null;
|
||||
if (jeeElt != null) {
|
||||
@@ -681,6 +685,8 @@ final class AuthenticationConfigBuilder {
|
||||
.rootBeanDefinition(J2eePreAuthenticatedProcessingFilter.class);
|
||||
filterBuilder.getRawBeanDefinition().setSource(this.pc.extractSource(jeeElt));
|
||||
filterBuilder.addPropertyValue("authenticationManager", authManager);
|
||||
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
||||
authenticationFilterSecurityContextHolderStrategyRef);
|
||||
BeanDefinitionBuilder adsBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class);
|
||||
adsBldr.addPropertyValue("userRoles2GrantedAuthoritiesMapper",
|
||||
|
||||
@@ -28,24 +28,30 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
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;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
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.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
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.SecurityMockMvcRequestPostProcessors.x509;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
@@ -90,6 +96,21 @@ public class X509ConfigurerTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void x509WhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
|
||||
this.spring.register(DefaultsInLambdaConfig.class, SecurityContextChangedListenerConfig.class).autowire();
|
||||
X509Certificate certificate = loadCert("rod.cer");
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/").with(x509(certificate)))
|
||||
.andExpect(authenticated().withUsername("rod"));
|
||||
// @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(PreAuthenticatedAuthenticationToken.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void x509WhenSubjectPrincipalRegexInLambdaThenUsesRegexToExtractPrincipal() throws Exception {
|
||||
this.spring.register(SubjectPrincipalRegexInLambdaConfig.class).autowire();
|
||||
|
||||
@@ -76,6 +76,7 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.openid.OpenIDAuthenticationFilter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
@@ -382,6 +383,19 @@ public class MiscHttpConfigTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingX509CustomSecurityContextHolderStrategyThenUses() throws Exception {
|
||||
System.setProperty("subject_principal_regex", "OU=(.*?)(?:,|$)");
|
||||
this.spring.configLocations(xml("X509WithSecurityContextHolderStrategy")).autowire();
|
||||
RequestPostProcessor x509 = x509(
|
||||
"classpath:org/springframework/security/config/http/MiscHttpConfigTests-certificate.pem");
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/protected").with(x509))
|
||||
.andExpect(status().isOk());
|
||||
// @formatter:on
|
||||
verify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenUsingInvalidLogoutSuccessUrlThenThrowsException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
@@ -654,6 +668,26 @@ public class MiscHttpConfigTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenJeeFilterCustomSecurityContextHolderStrategyThenUses() throws Exception {
|
||||
this.spring.configLocations(xml("JeeFilterWithSecurityContextHolderStrategy")).autowire();
|
||||
Principal user = mock(Principal.class);
|
||||
given(user.getName()).willReturn("joe");
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder rolesRequest = get("/roles")
|
||||
.principal(user)
|
||||
.with((request) -> {
|
||||
request.addUserRole("admin");
|
||||
request.addUserRole("user");
|
||||
request.addUserRole("unmapped");
|
||||
return request;
|
||||
});
|
||||
this.mvc.perform(rolesRequest)
|
||||
.andExpect(content().string("ROLE_admin,ROLE_user"));
|
||||
// @formatter:on
|
||||
verify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingCustomAuthenticationDetailsSourceRefThenAuthenticationSourcesDetailsAccordingly()
|
||||
throws Exception {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2002-2018 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.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<http security-context-holder-strategy-ref="ref">
|
||||
<jee mappable-roles="admin,user"/>
|
||||
</http>
|
||||
|
||||
<b:bean id="ref" class="org.mockito.Mockito" factory-method="spy">
|
||||
<b:constructor-arg>
|
||||
<b:bean class="org.springframework.security.config.MockSecurityContextHolderStrategy"/>
|
||||
</b:constructor-arg>
|
||||
</b:bean>
|
||||
|
||||
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||
<b:import resource="userservice.xml"/>
|
||||
</b:beans>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2002-2018 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.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/security
|
||||
https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<http security-context-holder-strategy-ref="ref">
|
||||
<x509 subject-principal-regex="${subject_principal_regex:(.*)}"/>
|
||||
<intercept-url pattern="/**" access="authenticated"/>
|
||||
</http>
|
||||
|
||||
<b:bean id="ref" class="org.mockito.Mockito" factory-method="spy">
|
||||
<b:constructor-arg>
|
||||
<b:bean class="org.springframework.security.config.MockSecurityContextHolderStrategy"/>
|
||||
</b:constructor-arg>
|
||||
</b:bean>
|
||||
|
||||
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
||||
|
||||
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||
<b:import resource="userservice.xml"/>
|
||||
</b:beans>
|
||||
Reference in New Issue
Block a user