Skip to content

Commit 78685cd

Browse files
committed
Merge branch '7.0.x'
2 parents 4942459 + cfdadfe commit 78685cd

18 files changed

Lines changed: 517 additions & 4 deletions

File tree

config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2033,7 +2033,9 @@ public HttpSecurity securityMatcher(RequestMatcher requestMatcher) {
20332033
*/
20342034
public HttpSecurity securityMatcher(String... patterns) {
20352035
List<RequestMatcher> matchers = new ArrayList<>();
2036-
PathPatternRequestMatcher.Builder builder = getSharedObject(PathPatternRequestMatcher.Builder.class);
2036+
ApplicationContext context = getSharedObject(ApplicationContext.class);
2037+
PathPatternRequestMatcher.Builder builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
2038+
.getIfUnique(() -> getSharedObject(PathPatternRequestMatcher.Builder.class));
20372039
for (String pattern : patterns) {
20382040
matchers.add(builder.matcher(pattern));
20392041
}

config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,7 @@ private Map<Class<?>, Object> createSharedObjects() {
239239
Map<Class<?>, Object> sharedObjects = new HashMap<>();
240240
sharedObjects.put(ApplicationContext.class, this.context);
241241
sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);
242-
sharedObjects.put(PathPatternRequestMatcher.Builder.class,
243-
this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
244-
.getIfUnique(() -> constructRequestMatcherBuilder(this.context)));
242+
sharedObjects.put(PathPatternRequestMatcher.Builder.class, constructRequestMatcherBuilder(this.context));
245243
return sharedObjects;
246244
}
247245

config/src/test/java/org/springframework/security/config/annotation/web/builders/WebSecurityTests.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.mock.web.MockServletContext;
3838
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3939
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
40+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
4041
import org.springframework.security.core.userdetails.PasswordEncodedUser;
4142
import org.springframework.security.core.userdetails.UserDetailsService;
4243
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@@ -110,6 +111,21 @@ public void requestRejectedHandlerInvokedWhenOperationalObservationRegistry() th
110111
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
111112
}
112113

114+
// gh-19128
115+
@Test
116+
public void ignoringWhenBuilderBeanWithBasePathThenHonorsBasePath() throws Exception {
117+
loadConfig(IgnoringBuilderBeanConfig.class);
118+
this.request.setServletPath("/spring");
119+
this.request.setRequestURI("/spring/path");
120+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
121+
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
122+
setup();
123+
this.request.setServletPath("");
124+
this.request.setRequestURI("/path");
125+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
126+
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
127+
}
128+
113129
public void loadConfig(Class<?>... configs) {
114130
this.context = new AnnotationConfigWebApplicationContext();
115131
this.context.register(configs);
@@ -201,6 +217,52 @@ String path() {
201217

202218
}
203219

220+
// gh-19128
221+
@EnableWebSecurity
222+
@Configuration
223+
@EnableWebMvc
224+
static class IgnoringBuilderBeanConfig {
225+
226+
@Bean
227+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
228+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
229+
bean.setBasePath("/spring");
230+
return bean;
231+
}
232+
233+
@Bean
234+
WebSecurityCustomizer webSecurityCustomizer() {
235+
return (web) -> web.ignoring().requestMatchers("/path");
236+
}
237+
238+
@Bean
239+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
240+
// @formatter:off
241+
http
242+
.httpBasic(withDefaults())
243+
.authorizeHttpRequests((requests) -> requests
244+
.anyRequest().denyAll());
245+
// @formatter:on
246+
return http.build();
247+
}
248+
249+
@Bean
250+
UserDetailsService userDetailsService() {
251+
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
252+
}
253+
254+
@RestController
255+
static class PathController {
256+
257+
@RequestMapping("/path")
258+
String path() {
259+
return "path";
260+
}
261+
262+
}
263+
264+
}
265+
204266
@Configuration
205267
@EnableWebSecurity
206268
static class RequestRejectedHandlerConfig {

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.security.config.test.SpringTestContext;
4141
import org.springframework.security.config.test.SpringTestContextExtension;
4242
import org.springframework.security.config.users.AuthenticationTestConfiguration;
43+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
4344
import org.springframework.security.core.authority.FactorGrantedAuthority;
4445
import org.springframework.security.core.context.SecurityContextChangedListener;
4546
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -153,6 +154,17 @@ public void loginWhenFormLoginConfiguredThenHasDefaultFailureUrl() throws Except
153154
// @formatter:on
154155
}
155156

157+
// gh-19128
158+
@Test
159+
public void loginWhenBuilderBeanWithBasePathThenLoginProcessingUrlIgnoresBasePath() throws Exception {
160+
this.spring.register(FormLoginBuilderBeanConfig.class).autowire();
161+
// @formatter:off
162+
this.mockMvc.perform(formLogin().user("invalid"))
163+
.andExpect(status().isFound())
164+
.andExpect(redirectedUrl("/login?error"));
165+
// @formatter:on
166+
}
167+
156168
@Test
157169
public void loginWhenFormLoginConfiguredThenHasDefaultSuccessUrl() throws Exception {
158170
this.spring.register(FormLoginConfig.class).autowire();
@@ -519,6 +531,37 @@ UserDetailsService userDetailsService() {
519531

520532
}
521533

534+
// gh-19128
535+
@Configuration
536+
@EnableWebSecurity
537+
@EnableWebMvc
538+
static class FormLoginBuilderBeanConfig {
539+
540+
@Bean
541+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
542+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
543+
bean.setBasePath("/spring");
544+
return bean;
545+
}
546+
547+
@Bean
548+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
549+
// @formatter:off
550+
http
551+
.authorizeHttpRequests((requests) -> requests
552+
.anyRequest().authenticated())
553+
.formLogin(withDefaults());
554+
// @formatter:on
555+
return http.build();
556+
}
557+
558+
@Bean
559+
UserDetailsService userDetailsService() {
560+
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
561+
}
562+
563+
}
564+
522565
@Configuration
523566
@EnableWebSecurity
524567
static class FormLoginInLambdaConfig {

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3636
import org.springframework.security.config.test.SpringTestContext;
3737
import org.springframework.security.config.test.SpringTestContextExtension;
38+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
3839
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3940
import org.springframework.security.core.userdetails.PasswordEncodedUser;
4041
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -127,6 +128,17 @@ public void logoutWhenInvokedTwiceThenUsesOriginalLogoutUrl() throws Exception {
127128
// @formatter:on
128129
}
129130

131+
// gh-19128
132+
@Test
133+
public void logoutWhenBuilderBeanWithBasePathThenLogoutUrlIgnoresBasePath() throws Exception {
134+
this.spring.register(LogoutBuilderBeanConfig.class).autowire();
135+
// @formatter:off
136+
this.mvc.perform(post("/logout").with(csrf()))
137+
.andExpect(status().isFound())
138+
.andExpect(redirectedUrl("/login?logout"));
139+
// @formatter:on
140+
}
141+
130142
// SEC-2311
131143
@Test
132144
public void logoutWhenGetRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {
@@ -524,6 +536,29 @@ UserDetailsService userDetailsService() {
524536

525537
}
526538

539+
// gh-19128
540+
@Configuration
541+
@EnableWebSecurity
542+
static class LogoutBuilderBeanConfig {
543+
544+
@Bean
545+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
546+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
547+
bean.setBasePath("/spring");
548+
return bean;
549+
}
550+
551+
@Bean
552+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
553+
// @formatter:off
554+
http
555+
.logout(withDefaults());
556+
// @formatter:on
557+
return http.build();
558+
}
559+
560+
}
561+
527562
@Configuration
528563
@EnableWebSecurity
529564
static class CsrfDisabledConfig {

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
2727
import org.springframework.security.config.test.SpringTestContext;
2828
import org.springframework.security.config.test.SpringTestContextExtension;
29+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
2930
import org.springframework.security.web.SecurityFilterChain;
3031
import org.springframework.test.web.servlet.MockMvc;
3132

@@ -66,6 +67,16 @@ public void whenChangePasswordPageSetThenSpecifiedChangePasswordPageUsed() throw
6667
.andExpect(redirectedUrl("/custom-change-password-page"));
6768
}
6869

70+
// gh-19128
71+
@Test
72+
public void changePasswordWhenBuilderBeanWithBasePathThenChangePasswordUrlIgnoresBasePath() throws Exception {
73+
this.spring.register(PasswordManagementBuilderBeanConfig.class).autowire();
74+
75+
this.mvc.perform(get("/.well-known/change-password"))
76+
.andExpect(status().isFound())
77+
.andExpect(redirectedUrl("/change-password"));
78+
}
79+
6980
@Test
7081
public void whenSettingNullChangePasswordPage() {
7182
PasswordManagementConfigurer configurer = new PasswordManagementConfigurer();
@@ -102,6 +113,29 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
102113

103114
}
104115

116+
// gh-19128
117+
@Configuration
118+
@EnableWebSecurity
119+
static class PasswordManagementBuilderBeanConfig {
120+
121+
@Bean
122+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
123+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
124+
bean.setBasePath("/spring");
125+
return bean;
126+
}
127+
128+
@Bean
129+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
130+
// @formatter:off
131+
return http
132+
.passwordManagement(withDefaults())
133+
.build();
134+
// @formatter:on
135+
}
136+
137+
}
138+
105139
@Configuration
106140
@EnableWebSecurity
107141
static class PasswordManagementWithCustomChangePasswordPageConfig {

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3535
import org.springframework.security.config.test.SpringTestContext;
3636
import org.springframework.security.config.test.SpringTestContextExtension;
37+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
3738
import org.springframework.security.core.userdetails.User;
3839
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
3940
import org.springframework.security.test.web.servlet.RequestCacheResultMatcher;
@@ -185,6 +186,21 @@ public void getWhenBookmarkedRequestIsWebSocketThenPostAuthenticationRedirectsTo
185186
this.mvc.perform(formLogin(session)).andExpect(redirectedUrl("/"));
186187
}
187188

189+
// gh-19128
190+
@Test
191+
public void getWhenBuilderBeanWithBasePathThenSavedRequestMatcherIgnoresBasePath() throws Exception {
192+
this.spring.register(RequestCacheBuilderBeanConfig.class, DefaultSecurityConfig.class).autowire();
193+
MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
194+
// @formatter:off
195+
MockHttpSession session = (MockHttpSession) this.mvc.perform(request)
196+
.andExpect(redirectedUrl("/login"))
197+
.andReturn()
198+
.getRequest()
199+
.getSession();
200+
// @formatter:on
201+
this.mvc.perform(formLogin(session)).andExpect(RequestCacheResultMatcher.redirectToCachedRequest());
202+
}
203+
188204
@Test
189205
public void getWhenBookmarkedRequestIsAllMediaTypeThenPostAuthenticationRemembers() throws Exception {
190206
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
@@ -401,6 +417,31 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
401417

402418
}
403419

420+
// gh-19128
421+
@Configuration
422+
@EnableWebSecurity
423+
static class RequestCacheBuilderBeanConfig {
424+
425+
@Bean
426+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
427+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
428+
bean.setBasePath("/spring");
429+
return bean;
430+
}
431+
432+
@Bean
433+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
434+
// @formatter:off
435+
http
436+
.authorizeHttpRequests((requests) -> requests
437+
.anyRequest().authenticated())
438+
.formLogin(withDefaults());
439+
// @formatter:on
440+
return http.build();
441+
}
442+
443+
}
444+
404445
@Configuration
405446
@EnableWebSecurity
406447
static class RequestCacheDisabledConfig {

0 commit comments

Comments
 (0)