Skip to content

Commit 76a5662

Browse files
committed
Use PortResolver Beans by Default
Closes gh-16664
1 parent 5f5427b commit 76a5662

File tree

8 files changed

+143
-2
lines changed

8 files changed

+143
-2
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java

+14
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@
2121

2222
import jakarta.servlet.http.HttpServletRequest;
2323

24+
import org.springframework.context.ApplicationContext;
2425
import org.springframework.http.MediaType;
2526
import org.springframework.security.authentication.AuthenticationDetailsSource;
2627
import org.springframework.security.authentication.AuthenticationManager;
2728
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2829
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
2930
import org.springframework.security.web.AuthenticationEntryPoint;
3031
import org.springframework.security.web.PortMapper;
32+
import org.springframework.security.web.PortResolver;
3133
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
3234
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
3335
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@@ -272,6 +274,10 @@ public void configure(B http) throws Exception {
272274
if (portMapper != null) {
273275
this.authenticationEntryPoint.setPortMapper(portMapper);
274276
}
277+
PortResolver portResolver = getBeanOrNull(http, PortResolver.class);
278+
if (portResolver != null) {
279+
this.authenticationEntryPoint.setPortResolver(portResolver);
280+
}
275281
RequestCache requestCache = http.getSharedObject(RequestCache.class);
276282
if (requestCache != null) {
277283
this.defaultSuccessHandler.setRequestCache(requestCache);
@@ -412,6 +418,14 @@ private void setLoginPage(String loginPage) {
412418
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
413419
}
414420

421+
private <C> C getBeanOrNull(B http, Class<C> clazz) {
422+
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
423+
if (context == null) {
424+
return null;
425+
}
426+
return context.getBeanProvider(clazz).getIfUnique();
427+
}
428+
415429
@SuppressWarnings("unchecked")
416430
private T getSelf() {
417431
return (T) this;

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import org.springframework.security.oauth2.core.user.OAuth2User;
8484
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
8585
import org.springframework.security.web.AuthenticationEntryPoint;
86+
import org.springframework.security.web.PortResolver;
8687
import org.springframework.security.web.RedirectStrategy;
8788
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
8889
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@@ -578,8 +579,13 @@ private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLogin
578579
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
579580
RequestMatcher formLoginNotEnabled = getFormLoginNotEnabledRequestMatcher(http);
580581
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
582+
LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);
583+
PortResolver portResolver = getBeanOrNull(ResolvableType.forClass(PortResolver.class));
584+
if (portResolver != null) {
585+
loginUrlEntryPoint.setPortResolver(portResolver);
586+
}
581587
entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher),
582-
formLoginNotEnabled), new LoginUrlAuthenticationEntryPoint(providerLoginPage));
588+
formLoginNotEnabled), loginUrlEntryPoint);
583589
DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
584590
loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint());
585591
return loginEntryPoint;

config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
5353
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
5454
import org.springframework.security.web.AuthenticationEntryPoint;
55+
import org.springframework.security.web.PortResolver;
5556
import org.springframework.security.web.authentication.AuthenticationConverter;
5657
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
5758
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@@ -344,8 +345,13 @@ private AuthenticationEntryPoint getLoginEntryPoint(B http, String providerLogin
344345
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
345346
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
346347
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
348+
LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage);
349+
PortResolver portResolver = getBeanOrNull(http, PortResolver.class);
350+
if (portResolver != null) {
351+
loginUrlEntryPoint.setPortResolver(portResolver);
352+
}
347353
entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)),
348-
new LoginUrlAuthenticationEntryPoint(providerLoginPage));
354+
loginUrlEntryPoint);
349355
DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
350356
loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint());
351357
return loginEntryPoint;

config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java

+4
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ private BeanReference createPortMapper(Element elt, ParserContext pc) {
240240
}
241241

242242
private RuntimeBeanReference createPortResolver(BeanReference portMapper, ParserContext pc) {
243+
String beanName = "portResolver";
244+
if (pc.getRegistry().containsBeanDefinition(beanName)) {
245+
return new RuntimeBeanReference(beanName);
246+
}
243247
RootBeanDefinition portResolver = new RootBeanDefinition(PortResolverImpl.class);
244248
portResolver.getPropertyValues().addPropertyValue("portMapper", portMapper);
245249
String portResolverName = pc.getReaderContext().generateBeanName(portResolver);

config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java

+37
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
3939
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
4040
import org.springframework.security.web.PortMapper;
41+
import org.springframework.security.web.PortResolver;
4142
import org.springframework.security.web.SecurityFilterChain;
4243
import org.springframework.security.web.access.ExceptionTranslationFilter;
4344
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@@ -378,6 +379,13 @@ public void configureWhenRegisteringObjectPostProcessorThenInvokedOnExceptionTra
378379
verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(ExceptionTranslationFilter.class));
379380
}
380381

382+
@Test
383+
public void configureWhenPortResolverBeanThenPortResolverUsed() throws Exception {
384+
this.spring.register(CustomPortResolverConfig.class).autowire();
385+
this.mockMvc.perform(get("/requires-authentication")).andExpect(status().is3xxRedirection());
386+
verify(this.spring.getContext().getBean(PortResolver.class)).getServerPort(any());
387+
}
388+
381389
@Configuration
382390
@EnableWebSecurity
383391
static class RequestCacheConfig {
@@ -723,6 +731,35 @@ static ObjectPostProcessor<Object> objectPostProcessor() {
723731

724732
}
725733

734+
@Configuration
735+
@EnableWebSecurity
736+
static class CustomPortResolverConfig {
737+
738+
@Bean
739+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
740+
// @formatter:off
741+
http
742+
.authorizeHttpRequests((requests) -> requests
743+
.anyRequest().authenticated()
744+
)
745+
.formLogin(withDefaults())
746+
.requestCache(withDefaults());
747+
return http.build();
748+
// @formatter:on
749+
}
750+
751+
@Bean
752+
PortResolver portResolver() {
753+
return mock(PortResolver.class);
754+
}
755+
756+
@Bean
757+
UserDetailsService userDetailsService() {
758+
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
759+
}
760+
761+
}
762+
726763
static class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {
727764

728765
@Override

config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java

+13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.security.core.AuthenticationException;
3636
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3737
import org.springframework.security.web.FilterChainProxy;
38+
import org.springframework.security.web.PortResolver;
3839
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
3940
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
4041
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
@@ -45,6 +46,7 @@
4546

4647
import static org.assertj.core.api.Assertions.assertThat;
4748
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
49+
import static org.mockito.ArgumentMatchers.any;
4850
import static org.mockito.Mockito.atLeastOnce;
4951
import static org.mockito.Mockito.verify;
5052
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
@@ -210,6 +212,17 @@ public void authenticateWhenLoginPageIsSlashLoginAndAuthenticationFailsThenRedir
210212
// @formatter:on
211213
}
212214

215+
@Test
216+
public void portResolver() throws Exception {
217+
this.spring.configLocations(this.xml("PortResolverBean")).autowire();
218+
// @formatter:off
219+
this.mvc.perform(get("/requires-authentication"))
220+
.andExpect(status().is3xxRedirection());
221+
// @formatter:on
222+
PortResolver portResolver = this.spring.getContext().getBean(PortResolver.class);
223+
verify(portResolver, atLeastOnce()).getServerPort(any());
224+
}
225+
213226
private Filter getFilter(ApplicationContext context, Class<? extends Filter> filterClass) {
214227
FilterChainProxy filterChain = context.getBean(BeanIds.FILTER_CHAIN_PROXY, FilterChainProxy.class);
215228
List<Filter> filters = filterChain.getFilters("/any");

config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt

+24
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import org.springframework.security.core.userdetails.User
3434
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin
3535
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
3636
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated
37+
import org.springframework.security.web.PortResolver
3738
import org.springframework.security.web.SecurityFilterChain
3839
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler
3940
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler
@@ -240,6 +241,29 @@ class FormLoginDslTests {
240241
}
241242
}
242243

244+
@Test
245+
fun `portResolerBean is used`() {
246+
this.spring.register(PortResolverBeanConfig::class.java, AllSecuredConfig::class.java, UserConfig::class.java).autowire()
247+
248+
val portResolver = this.spring.context.getBean(PortResolver::class.java)
249+
every { portResolver.getServerPort(any()) }.returns(1234)
250+
this.mockMvc.get("/")
251+
.andExpect {
252+
status().isFound
253+
redirectedUrl("http://localhost:1234/login")
254+
}
255+
256+
verify { portResolver.getServerPort(any()) }
257+
}
258+
259+
@Configuration
260+
open class PortResolverBeanConfig {
261+
@Bean
262+
open fun portResolverBean(): PortResolver {
263+
return mockk()
264+
}
265+
}
266+
243267
@Test
244268
fun `login when custom failure url then used`() {
245269
this.spring.register(FailureHandlerConfig::class.java, UserConfig::class.java).autowire()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2002-2022 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xmlns="http://www.springframework.org/schema/security"
21+
xsi:schemaLocation="
22+
http://www.springframework.org/schema/security
23+
https://www.springframework.org/schema/security/spring-security.xsd
24+
http://www.springframework.org/schema/beans
25+
https://www.springframework.org/schema/beans/spring-beans.xsd">
26+
27+
<b:bean id="portResolver" class="org.mockito.Mockito" factory-method="mock" scope="singleton">
28+
<b:constructor-arg value="org.springframework.security.web.PortResolver" type="java.lang.Class"/>
29+
</b:bean>
30+
31+
<http auto-config="true">
32+
<csrf disabled="true"/>
33+
<intercept-url pattern="/**" access="authenticated"/>
34+
</http>
35+
36+
<b:import resource="userservice.xml"/>
37+
</b:beans>

0 commit comments

Comments
 (0)