From a411735af030b0ceede5be4d26be42f268d8c066 Mon Sep 17 00:00:00 2001 From: Michal Okosy Date: Sun, 10 Mar 2024 12:53:16 +0100 Subject: [PATCH 1/2] Use relative URLs in /login redirects Closes gh-7273 --- .../web/builders/NamespaceHttpTests.java | 6 ++--- .../DefaultLoginPageConfigurerTests.java | 2 +- .../ExceptionHandlingConfigurerTests.java | 5 ++-- .../configurers/FormLoginConfigurerTests.java | 8 +++--- .../NamespaceHttpFormLoginTests.java | 8 +++--- .../configurers/NamespaceRememberMeTests.java | 6 ++--- .../RememberMeConfigurerTests.java | 4 +-- .../RequestCacheConfigurerTests.java | 20 +++++++------- .../client/OAuth2LoginConfigurerTests.java | 14 +++++----- .../OAuth2ResourceServerConfigurerTests.java | 4 +-- .../saml2/Saml2LoginConfigurerTests.java | 7 ++--- .../security/config/http/CsrfConfigTests.java | 8 +++--- .../config/http/FormLoginConfigTests.java | 6 ++--- .../security/config/http/HttpConfigTests.java | 8 +++--- .../config/http/MiscHttpConfigTests.java | 6 ++--- .../OAuth2LoginBeanDefinitionParserTests.java | 14 +++++----- .../http/PlaceHolderAndELConfigTests.java | 6 ++--- .../Saml2LoginBeanDefinitionParserTests.java | 6 ++--- ...yContextHolderAwareRequestConfigTests.java | 16 ++++++------ .../web/ExceptionHandlingDslTests.kt | 8 +++--- .../annotation/web/FormLoginDslTests.kt | 4 +-- .../annotation/web/RememberMeDslTests.kt | 6 ++--- .../LoginUrlAuthenticationEntryPoint.java | 26 +++++++------------ ...LoginUrlAuthenticationEntryPointTests.java | 14 +++++++--- 24 files changed, 105 insertions(+), 107 deletions(-) diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java index e24b8d8f101..7299e3ebc8b 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -77,7 +77,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** @@ -167,7 +167,7 @@ public void configureWhenAuthenticationEntryPointSetAndRequestUnauthorizedThenRe // @formatter:off this.mockMvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrlPattern("**/entry-point")); + .andExpect(redirectedUrl("/entry-point")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java index 6d683c4899a..b519769bdb5 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java @@ -82,7 +82,7 @@ public class DefaultLoginPageConfigurerTests { @Test public void getWhenFormLoginEnabledThenRedirectsToLoginPage() throws Exception { this.spring.register(DefaultLoginPageConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("/login")); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java index d89526127e2..cd57c7bd1ba 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -213,8 +213,7 @@ public void getWhenCustomSecurityContextHolderStrategyThenUsed() throws Exceptio @Test public void getWhenUsingDefaultsAndUnauthenticatedThenRedirectsToLogin() throws Exception { this.spring.register(DefaultHttpConfig.class).autowire(); - this.mvc.perform(get("/").header(HttpHeaders.ACCEPT, "bogus/type")) - .andExpect(redirectedUrl("http://localhost/login")); + this.mvc.perform(get("/").header(HttpHeaders.ACCEPT, "bogus/type")).andExpect(redirectedUrl("/login")); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java index 49b8ed2a1af..663b67bcbfa 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -162,7 +162,7 @@ public void requestProtectedWhenFormLoginConfiguredThenRedirectsToLogin() throws // @formatter:off this.mockMvc.perform(get("/private")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -217,7 +217,7 @@ public void requestProtectedWhenFormLoginDefaultsInLambdaThenRedirectsToLogin() // @formatter:off this.mockMvc.perform(get("/private")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -331,7 +331,7 @@ public void failureUrlWhenPermitAllAndFailureHandlerThenSecured() throws Excepti // @formatter:off this.mockMvc.perform(get("/login?error")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java index c1bd55dbdbd..2d46951de1b 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -65,7 +65,7 @@ public class NamespaceHttpFormLoginTests { @Test public void formLoginWhenDefaultConfigurationThenMatchesNamespace() throws Exception { this.spring.register(FormLoginConfig.class, UserDetailsServiceConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("/login")); this.mvc.perform(post("/login").with(csrf())).andExpect(redirectedUrl("/login?error")); // @formatter:off MockHttpServletRequestBuilder loginRequest = post("/login") @@ -79,7 +79,7 @@ public void formLoginWhenDefaultConfigurationThenMatchesNamespace() throws Excep @Test public void formLoginWithCustomEndpointsThenBehaviorMatchesNamespace() throws Exception { this.spring.register(FormLoginCustomConfig.class, UserDetailsServiceConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/authentication/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("/authentication/login")); this.mvc.perform(post("/authentication/login/process").with(csrf())) .andExpect(redirectedUrl("/authentication/login?failed")); // @formatter:off @@ -94,7 +94,7 @@ public void formLoginWithCustomEndpointsThenBehaviorMatchesNamespace() throws Ex @Test public void formLoginWithCustomHandlersThenBehaviorMatchesNamespace() throws Exception { this.spring.register(FormLoginCustomRefsConfig.class, UserDetailsServiceConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("/login")); this.mvc.perform(post("/login").with(csrf())).andExpect(redirectedUrl("/custom/failure")); verifyBean(WebAuthenticationDetailsSource.class).buildDetails(any(HttpServletRequest.class)); // @formatter:off diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java index c55f865db8d..2c448484e50 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -104,7 +104,7 @@ public void rememberMeLoginWhenUsingDefaultsThenMatchesNamespace() throws Except .with(csrf()) .cookie(rememberMe); this.mvc.perform(authenticationClassRequest) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn(); // @formatter:on } @@ -150,7 +150,7 @@ public void rememberMeLoginWhenKeyDeclaredThenMatchesNamespace() throws Exceptio // @formatter:off this.mvc.perform(somewhereRequest) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); MockHttpServletRequestBuilder loginWithRememberme = post("/login").with(rememberMeLogin()); Cookie withKey = this.mvc.perform(loginWithRememberme) .andReturn() diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java index e3cb83f76fd..9277158ba22 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -240,7 +240,7 @@ public void getWhenRememberMeCookieAndLoggedOutThenRedirectsToLogin() throws Exc .with(csrf()) .cookie(expiredRememberMeCookie); // @formatter:on - this.mvc.perform(expiredRequest).andExpect(redirectedUrl("http://localhost/login")); + this.mvc.perform(expiredRequest).andExpect(redirectedUrl("/login")); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java index f22e55043d9..09ada968b68 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -90,7 +90,7 @@ public void getWhenBookmarkedUrlIsFaviconIcoThenPostAuthenticationRedirectsToRoo this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire(); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(get("/favicon.ico")) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -104,7 +104,7 @@ public void getWhenBookmarkedUrlIsFaviconPngThenPostAuthenticationRedirectsToRoo this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire(); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(get("/favicon.png")) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -120,7 +120,7 @@ public void getWhenBookmarkedRequestIsApplicationJsonThenPostAuthenticationRedir MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -140,7 +140,7 @@ public void getWhenBookmarkedRequestIsXRequestedWithThenPostAuthenticationRedire .header("X-Requested-With", "XMLHttpRequest"); MockHttpSession session = (MockHttpSession) this.mvc .perform(xRequestedWith) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -157,7 +157,7 @@ public void getWhenBookmarkedRequestIsTextEventStreamThenPostAuthenticationRedir MediaType.TEXT_EVENT_STREAM); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -174,7 +174,7 @@ public void getWhenBookmarkedRequestIsAllMediaTypeThenPostAuthenticationRemember MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.ALL); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -188,7 +188,7 @@ public void getWhenBookmarkedRequestIsTextHtmlThenPostAuthenticationRemembers() MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -203,7 +203,7 @@ public void getWhenBookmarkedRequestIsChromeThenPostAuthenticationRemembers() th MockHttpServletRequestBuilder request = get("/messages") .header(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); @@ -218,7 +218,7 @@ public void getWhenBookmarkedRequestIsRequestedWithAndroidThenPostAuthentication MockHttpServletRequestBuilder request = get("/messages") .header("X-Requested-With", "com.android"); MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java index b56d047a5f7..65a56cdb7aa 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java @@ -437,7 +437,7 @@ public void oauth2LoginWithOneClientConfiguredThenRedirectForAuthorization() thr this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("http://localhost/oauth2/authorization/google"); + assertThat(this.response.getRedirectedUrl()).matches("/oauth2/authorization/google"); } // gh-6802 @@ -448,7 +448,7 @@ public void oauth2LoginWithOneClientConfiguredAndFormLoginThenRedirectDefaultLog this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("http://localhost/login"); + assertThat(this.response.getRedirectedUrl()).matches("/login"); } // gh-5347 @@ -461,7 +461,7 @@ public void oauth2LoginWithOneClientConfiguredAndRequestFaviconNotAuthenticatedT this.request.setServletPath(requestUri); this.request.addHeader(HttpHeaders.ACCEPT, new MediaType("image", "*").toString()); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("http://localhost/login"); + assertThat(this.response.getRedirectedUrl()).matches("/login"); } // gh-5347 @@ -472,7 +472,7 @@ public void oauth2LoginWithMultipleClientsConfiguredThenRedirectDefaultLoginPage this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("http://localhost/login"); + assertThat(this.response.getRedirectedUrl()).matches("/login"); } // gh-6812 @@ -521,7 +521,7 @@ public void oauth2LoginWithOneAuthorizationCodeClientAndOtherClientsConfiguredTh this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("http://localhost/oauth2/authorization/google"); + assertThat(this.response.getRedirectedUrl()).matches("/oauth2/authorization/google"); } @Test @@ -531,7 +531,7 @@ public void oauth2LoginWithCustomLoginPageThenRedirectCustomLoginPage() throws E this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("http://localhost/custom-login"); + assertThat(this.response.getRedirectedUrl()).matches("/custom-login"); } @Test @@ -541,7 +541,7 @@ public void requestWhenOauth2LoginWithCustomLoginPageInLambdaThenRedirectCustomL this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("http://localhost/custom-login"); + assertThat(this.response.getRedirectedUrl()).matches("/custom-login"); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index c247a6d7fed..2dad6916fe6 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -1210,7 +1210,7 @@ public void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedBy MvcResult result = this.mvc.perform(get("/authenticated") .header("Accept", "text/html")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn(); // @formatter:on assertThat(result.getRequest().getSession(false)).isNotNull(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java index 6d874a583d6..3a912072908 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -412,10 +412,11 @@ public void getFaviconWhenDefaultConfigurationThenDoesNotSaveAuthnRequest() thro this.spring.register(Saml2LoginConfig.class).autowire(); this.mvc.perform(get("/favicon.ico").accept(MediaType.TEXT_HTML)) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); this.mvc.perform(get("/").accept(MediaType.TEXT_HTML)) .andExpect(status().isFound()) - .andExpect(header().string("Location", startsWith("http://localhost/saml2/authenticate"))); + .andExpect(header().string("Location", startsWith("/saml2/authenticate"))); + } @Test diff --git a/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java b/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java index 901945e73aa..781cce82e92 100644 --- a/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -342,7 +342,7 @@ public void postWhenHasCsrfTokenButSessionExpiresThenRequestIsCancelledAfterSucc this.spring.configLocations(this.xml("CsrfEnabled")).autowire(); // simulates a request that has no authentication (e.g. session time-out) MvcResult result = this.mvc.perform(post("/authenticated").with(csrf())) - .andExpect(redirectedUrl("http://localhost/login")) + .andExpect(redirectedUrl("/login")) .andReturn(); MockHttpSession session = (MockHttpSession) result.getRequest().getSession(); // if the request cache is consulted, then it will redirect back to /some-url, @@ -363,9 +363,7 @@ public void getWhenHasCsrfTokenButSessionExpiresThenRequestIsRememeberedAfterSuc throws Exception { this.spring.configLocations(this.xml("CsrfEnabled")).autowire(); // simulates a request that has no authentication (e.g. session time-out) - MvcResult result = this.mvc.perform(get("/authenticated")) - .andExpect(redirectedUrl("http://localhost/login")) - .andReturn(); + MvcResult result = this.mvc.perform(get("/authenticated")).andExpect(redirectedUrl("/login")).andReturn(); MockHttpSession session = (MockHttpSession) result.getRequest().getSession(); // if the request cache is consulted, then it will redirect back to /some-url, // which we do want diff --git a/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java b/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java index 52237273df3..b054762aac3 100644 --- a/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2024 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. @@ -73,7 +73,7 @@ public void getProtectedPageWhenFormLoginConfiguredThenRedirectsToDefaultLoginPa this.spring.configLocations(this.xml("WithAntRequestMatcher")).autowire(); // @formatter:off this.mvc.perform(get("/")) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -107,7 +107,7 @@ public void authenticateWhenConfiguredWithSpelThenRedirectsAccordingly() throws this.mvc.perform(invalidPassword) .andExpect(redirectedUrl(WebConfigUtilsTests.URL + "/failure")); this.mvc.perform(get("/")) - .andExpect(redirectedUrl("http://localhost" + WebConfigUtilsTests.URL + "/login")); + .andExpect(redirectedUrl(WebConfigUtilsTests.URL + "/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java index 9a4e3b041e1..62b4ab99dd1 100644 --- a/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java @@ -71,7 +71,7 @@ public void getWhenUsingMinimalConfigurationThenRedirectsToLogin() throws Except // @formatter:off this.mvc.perform(get("/")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -81,7 +81,7 @@ public void getWhenUsingMinimalAuthorizationManagerThenRedirectsToLogin() throws // @formatter:off this.mvc.perform(get("/")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -95,7 +95,7 @@ public void getWhenUsingAuthorizationManagerThenRedirectsToLogin() throws Except // @formatter:off this.mvc.perform(get("/")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on verify(authorizationManager).check(any(), any()); } @@ -109,7 +109,7 @@ public void getWhenUsingMinimalConfigurationThenPreventsSessionAsUrlParameter() proxy.doFilter(request, new EncodeUrlDenyingHttpServletResponseWrapper(response), (req, resp) -> { }); assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY); - assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login"); + assertThat(response.getRedirectedUrl()).isEqualTo("/login"); } @Test diff --git a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java index 180bd2ec532..b08a3c868a0 100644 --- a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -573,7 +573,7 @@ public void configureWhenUsingDisableUrlRewritingThenRedirectIsNotEncodedByRespo proxy.doFilter(request, new EncodeUrlDenyingHttpServletResponseWrapper(response), (req, resp) -> { }); assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY); - assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login"); + assertThat(response.getRedirectedUrl()).isEqualTo("/login"); } @Test @@ -802,7 +802,7 @@ public void authenticateWhenUsingPortMapperThenRedirectsAppropriately() throws E this.spring.configLocations(xml("PortsMappedRequiresHttps")).autowire(); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(get("https://localhost:9080/protected")) - .andExpect(redirectedUrl("https://localhost:9443/login")) + .andExpect(redirectedUrl("/login")) .andReturn() .getRequest() .getSession(false); diff --git a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java index b632f834de2..6c1e24dcee9 100644 --- a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -174,7 +174,7 @@ public void requestWhenSingleClientRegistrationThenAutoRedirect() throws Excepti // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/oauth2/authorization/google-login")); + .andExpect(redirectedUrl("/oauth2/authorization/google-login")); // @formatter:on verify(this.requestCache).saveRequest(any(), any()); } @@ -187,7 +187,7 @@ public void requestWhenSingleClientRegistrationAndRequestFaviconNotAuthenticated // @formatter:off this.mvc.perform(get("/favicon.ico").accept(new MediaType("image", "*"))) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -199,7 +199,7 @@ public void requestWhenSingleClientRegistrationAndRequestXHRNotAuthenticatedThen // @formatter:off this.mvc.perform(get("/").header("X-Requested-With", "XMLHttpRequest")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -411,7 +411,7 @@ public void requestWhenMultiClientRegistrationThenRedirectDefaultLoginPage() thr // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -421,7 +421,7 @@ public void requestWhenCustomLoginPageThenRedirectCustomLoginPage() throws Excep // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/custom-login")); + .andExpect(redirectedUrl("/custom-login")); // @formatter:on } @@ -433,7 +433,7 @@ public void requestWhenSingleClientRegistrationAndFormLoginConfiguredThenRedirec // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java b/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java index ffe686efc16..0c1710f98b9 100644 --- a/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2024 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. @@ -73,7 +73,7 @@ public void loginWhenUsingPlaceholderThenInterceptUrlsAndFormLoginWorks() throws // login-page setting // @formatter:off this.mvc.perform(get("/secured")) - .andExpect(redirectedUrl("http://localhost/loginPage")); + .andExpect(redirectedUrl("/loginPage")); // login-processing-url setting // default-target-url setting this.mvc.perform(post("/loginPage").param("username", "user").param("password", "password")) @@ -98,7 +98,7 @@ public void loginWhenUsingSpELThenInterceptUrlsAndFormLoginWorks() throws Except // login-page setting // @formatter:off this.mvc.perform(get("/secured")) - .andExpect(redirectedUrl("http://localhost/loginPage")); + .andExpect(redirectedUrl("/loginPage")); // login-processing-url setting // default-target-url setting this.mvc.perform(post("/loginPage").param("username", "user").param("password", "password")) diff --git a/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java index 9eb168233ab..fad02a9ae0c 100644 --- a/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -161,7 +161,7 @@ public void requestWhenSingleRelyingPartyRegistrationThenAutoRedirect() throws E // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/saml2/authenticate/one")); + .andExpect(redirectedUrl("/saml2/authenticate/one")); // @formatter:on verify(this.requestCache).saveRequest(any(), any()); } @@ -172,7 +172,7 @@ public void requestWhenMultiRelyingPartyRegistrationThenRedirectToLoginWithRelyi // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java b/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java index 713a03b846b..5a7d0a90382 100644 --- a/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2024 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. @@ -78,7 +78,7 @@ public void servletAuthenticateWhenUsingDefaultConfigurationThenUsesSpringSecuri // @formatter:off this.mvc.perform(get("/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -114,7 +114,7 @@ public void servletAuthenticateWhenUsingFormLoginThenUsesSpringSecurity() throws // @formatter:off this.mvc.perform(get("/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); // @formatter:on } @@ -137,10 +137,10 @@ public void servletAuthenticateWhenUsingMultipleHttpConfigsThenUsesSpringSecurit // @formatter:off this.mvc.perform(get("/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login")); + .andExpect(redirectedUrl("/login")); this.mvc.perform(get("/v2/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/login2")); + .andExpect(redirectedUrl("/login2")); // @formatter:on } @@ -177,10 +177,10 @@ public void servletLogoutWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() th @Test public void servletLogoutWhenUsingCustomLogoutThenUsesSpringSecurity() throws Exception { this.spring.configLocations(this.xml("Logout")).autowire(); - this.mvc.perform(get("/authenticate")) - .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/signin")); // @formatter:off + this.mvc.perform(get("/authenticate")) + .andExpect(status().isFound()) + .andExpect(redirectedUrl("/signin")); MvcResult result = this.mvc.perform(get("/good-login")) .andReturn(); // @formatter:on diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt index ed3e409cfff..2aea44c6ffb 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -217,7 +217,7 @@ class ExceptionHandlingDslTests { this.mockMvc.get("/") .andExpect { status { isFound() } - redirectedUrl("http://localhost/custom-login") + redirectedUrl("/custom-login") } } @@ -246,13 +246,13 @@ class ExceptionHandlingDslTests { this.mockMvc.get("/secured1") .andExpect { status { isFound() } - redirectedUrl("http://localhost/custom-login1") + redirectedUrl("/custom-login1") } this.mockMvc.get("/secured2") .andExpect { status { isFound() } - redirectedUrl("http://localhost/custom-login2") + redirectedUrl("/custom-login2") } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt index 965c361b4a3..5b00105247c 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt @@ -143,7 +143,7 @@ class FormLoginDslTests { this.mockMvc.get("/") .andExpect { status { isFound() } - redirectedUrl("http://localhost/login") + redirectedUrl("/login") } } @@ -169,7 +169,7 @@ class FormLoginDslTests { this.mockMvc.get("/") .andExpect { status { isFound() } - redirectedUrl("http://localhost/log-in") + redirectedUrl("/log-in") } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt index b73b41f50de..a3a07cc0106 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -153,7 +153,7 @@ internal class RememberMeDslTests { cookie(expiredRememberMeCookie) }.andExpect { status { isFound() } - redirectedUrl("http://localhost/login") + redirectedUrl("/login") } } @@ -229,7 +229,7 @@ internal class RememberMeDslTests { cookie(withoutKeyRememberMeCookie) }.andExpect { status { isFound() } - redirectedUrl("http://localhost/login") + redirectedUrl("/login") } val keyMvcResult = mockMvc.post("/login") { loginRememberMeRequest() diff --git a/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java b/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java index 3bf2c6f0dbf..60cf4eef4c1 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java +++ b/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java @@ -61,6 +61,7 @@ * @author colin sampaleanu * @author Omri Spector * @author Luke Taylor + * @author Michal Okosy * @since 3.0 */ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean { @@ -143,29 +144,22 @@ public void commence(HttpServletRequest request, HttpServletResponse response, protected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { String loginForm = determineUrlToUseForThisRequest(request, response, authException); - if (UrlUtils.isAbsoluteUrl(loginForm)) { + if (UrlUtils.isAbsoluteUrl(loginForm) || !this.forceHttps || "https".equals(request.getScheme())) { return loginForm; } int serverPort = this.portResolver.getServerPort(request); - String scheme = request.getScheme(); + Integer httpsPort = this.portMapper.lookupHttpsPort(serverPort); + if (httpsPort == null) { + logger.warn(LogMessage.format("Unable to redirect to HTTPS as no port mapping found for HTTP port %s", + serverPort)); + return loginForm; + } RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder(); - urlBuilder.setScheme(scheme); + urlBuilder.setScheme("https"); urlBuilder.setServerName(request.getServerName()); - urlBuilder.setPort(serverPort); + urlBuilder.setPort(httpsPort); urlBuilder.setContextPath(request.getContextPath()); urlBuilder.setPathInfo(loginForm); - if (this.forceHttps && "http".equals(scheme)) { - Integer httpsPort = this.portMapper.lookupHttpsPort(serverPort); - if (httpsPort != null) { - // Overwrite scheme and port in the redirect URL - urlBuilder.setScheme("https"); - urlBuilder.setPort(httpsPort); - } - else { - logger.warn(LogMessage.format("Unable to redirect to HTTPS as no port mapping found for HTTP port %s", - serverPort)); - } - } return urlBuilder.getUrl(); } diff --git a/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java b/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java index 77b49be1a16..ad699da8ea9 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java @@ -129,12 +129,18 @@ public void testHttpsOperationFromOriginalHttpsUrl() throws Exception { ep.setPortResolver(new MockPortResolver(80, 443)); ep.afterPropertiesSet(); ep.commence(request, response, null); - assertThat(response.getRedirectedUrl()).isEqualTo("https://www.example.com/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); request.setServerPort(8443); response = new MockHttpServletResponse(); ep.setPortResolver(new MockPortResolver(8080, 8443)); ep.commence(request, response, null); - assertThat(response.getRedirectedUrl()).isEqualTo("https://www.example.com:8443/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + // access to https via http port + request.setServerPort(8080); + response = new MockHttpServletResponse(); + ep.setPortResolver(new MockPortResolver(8080, 8443)); + ep.commence(request, response, null); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); } @Test @@ -152,7 +158,7 @@ public void testNormalOperation() throws Exception { request.setServerPort(80); MockHttpServletResponse response = new MockHttpServletResponse(); ep.commence(request, response, null); - assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); } @Test @@ -172,7 +178,7 @@ public void testOperationWhenHttpsRequestsButHttpsPortUnknown() throws Exception ep.commence(request, response, null); // Response doesn't switch to HTTPS, as we didn't know HTTP port 8888 to HTTP port // mapping - assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost:8888/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); } @Test From 258beea62f2dbd456449c5a9538778b52ca8432c Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:12:41 -0700 Subject: [PATCH 2/2] Add setFavorRelativeUris This places the new functionality behind a setting so that we can remain passive until we can change the setting in the next major release. Issue gh-7273 --- .../web/builders/NamespaceHttpTests.java | 6 +- .../DefaultLoginPageConfigurerTests.java | 4 +- .../ExceptionHandlingConfigurerTests.java | 5 +- .../configurers/FormLoginConfigurerTests.java | 8 +- .../NamespaceHttpFormLoginTests.java | 8 +- .../configurers/NamespaceRememberMeTests.java | 6 +- .../RememberMeConfigurerTests.java | 4 +- .../RequestCacheConfigurerTests.java | 20 ++-- .../client/OAuth2LoginConfigurerTests.java | 14 +-- .../OAuth2ResourceServerConfigurerTests.java | 4 +- .../saml2/Saml2LoginConfigurerTests.java | 7 +- .../security/config/http/CsrfConfigTests.java | 8 +- .../config/http/FormLoginConfigTests.java | 6 +- .../security/config/http/HttpConfigTests.java | 10 +- .../config/http/MiscHttpConfigTests.java | 6 +- .../OAuth2LoginBeanDefinitionParserTests.java | 14 +-- .../http/PlaceHolderAndELConfigTests.java | 6 +- .../Saml2LoginBeanDefinitionParserTests.java | 6 +- ...yContextHolderAwareRequestConfigTests.java | 16 +-- .../web/ExceptionHandlingDslTests.kt | 8 +- .../annotation/web/FormLoginDslTests.kt | 4 +- .../annotation/web/RememberMeDslTests.kt | 6 +- docs/modules/ROOT/pages/migration-7/web.adoc | 104 ++++++++++++++++++ .../LoginUrlAuthenticationEntryPoint.java | 46 +++++++- ...LoginUrlAuthenticationEntryPointTests.java | 60 +++++++++- 25 files changed, 288 insertions(+), 98 deletions(-) create mode 100644 docs/modules/ROOT/pages/migration-7/web.adoc diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java index 7299e3ebc8b..e24b8d8f101 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -77,7 +77,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** @@ -167,7 +167,7 @@ public void configureWhenAuthenticationEntryPointSetAndRequestUnauthorizedThenRe // @formatter:off this.mockMvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/entry-point")); + .andExpect(redirectedUrlPattern("**/entry-point")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java index b519769bdb5..f4646fe6f53 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2023 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. @@ -82,7 +82,7 @@ public class DefaultLoginPageConfigurerTests { @Test public void getWhenFormLoginEnabledThenRedirectsToLoginPage() throws Exception { this.spring.register(DefaultLoginPageConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java index cd57c7bd1ba..d89526127e2 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -213,7 +213,8 @@ public void getWhenCustomSecurityContextHolderStrategyThenUsed() throws Exceptio @Test public void getWhenUsingDefaultsAndUnauthenticatedThenRedirectsToLogin() throws Exception { this.spring.register(DefaultHttpConfig.class).autowire(); - this.mvc.perform(get("/").header(HttpHeaders.ACCEPT, "bogus/type")).andExpect(redirectedUrl("/login")); + this.mvc.perform(get("/").header(HttpHeaders.ACCEPT, "bogus/type")) + .andExpect(redirectedUrl("http://localhost/login")); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java index 663b67bcbfa..49b8ed2a1af 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -162,7 +162,7 @@ public void requestProtectedWhenFormLoginConfiguredThenRedirectsToLogin() throws // @formatter:off this.mockMvc.perform(get("/private")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -217,7 +217,7 @@ public void requestProtectedWhenFormLoginDefaultsInLambdaThenRedirectsToLogin() // @formatter:off this.mockMvc.perform(get("/private")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -331,7 +331,7 @@ public void failureUrlWhenPermitAllAndFailureHandlerThenSecured() throws Excepti // @formatter:off this.mockMvc.perform(get("/login?error")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java index 2d46951de1b..c1bd55dbdbd 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -65,7 +65,7 @@ public class NamespaceHttpFormLoginTests { @Test public void formLoginWhenDefaultConfigurationThenMatchesNamespace() throws Exception { this.spring.register(FormLoginConfig.class, UserDetailsServiceConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); this.mvc.perform(post("/login").with(csrf())).andExpect(redirectedUrl("/login?error")); // @formatter:off MockHttpServletRequestBuilder loginRequest = post("/login") @@ -79,7 +79,7 @@ public void formLoginWhenDefaultConfigurationThenMatchesNamespace() throws Excep @Test public void formLoginWithCustomEndpointsThenBehaviorMatchesNamespace() throws Exception { this.spring.register(FormLoginCustomConfig.class, UserDetailsServiceConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("/authentication/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/authentication/login")); this.mvc.perform(post("/authentication/login/process").with(csrf())) .andExpect(redirectedUrl("/authentication/login?failed")); // @formatter:off @@ -94,7 +94,7 @@ public void formLoginWithCustomEndpointsThenBehaviorMatchesNamespace() throws Ex @Test public void formLoginWithCustomHandlersThenBehaviorMatchesNamespace() throws Exception { this.spring.register(FormLoginCustomRefsConfig.class, UserDetailsServiceConfig.class).autowire(); - this.mvc.perform(get("/")).andExpect(redirectedUrl("/login")); + this.mvc.perform(get("/")).andExpect(redirectedUrl("http://localhost/login")); this.mvc.perform(post("/login").with(csrf())).andExpect(redirectedUrl("/custom/failure")); verifyBean(WebAuthenticationDetailsSource.class).buildDetails(any(HttpServletRequest.class)); // @formatter:off diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java index 2c448484e50..c55f865db8d 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -104,7 +104,7 @@ public void rememberMeLoginWhenUsingDefaultsThenMatchesNamespace() throws Except .with(csrf()) .cookie(rememberMe); this.mvc.perform(authenticationClassRequest) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn(); // @formatter:on } @@ -150,7 +150,7 @@ public void rememberMeLoginWhenKeyDeclaredThenMatchesNamespace() throws Exceptio // @formatter:off this.mvc.perform(somewhereRequest) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); MockHttpServletRequestBuilder loginWithRememberme = post("/login").with(rememberMeLogin()); Cookie withKey = this.mvc.perform(loginWithRememberme) .andReturn() diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java index 9277158ba22..e3cb83f76fd 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2023 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. @@ -240,7 +240,7 @@ public void getWhenRememberMeCookieAndLoggedOutThenRedirectsToLogin() throws Exc .with(csrf()) .cookie(expiredRememberMeCookie); // @formatter:on - this.mvc.perform(expiredRequest).andExpect(redirectedUrl("/login")); + this.mvc.perform(expiredRequest).andExpect(redirectedUrl("http://localhost/login")); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java index 09ada968b68..f22e55043d9 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -90,7 +90,7 @@ public void getWhenBookmarkedUrlIsFaviconIcoThenPostAuthenticationRedirectsToRoo this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire(); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(get("/favicon.ico")) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -104,7 +104,7 @@ public void getWhenBookmarkedUrlIsFaviconPngThenPostAuthenticationRedirectsToRoo this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire(); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(get("/favicon.png")) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -120,7 +120,7 @@ public void getWhenBookmarkedRequestIsApplicationJsonThenPostAuthenticationRedir MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -140,7 +140,7 @@ public void getWhenBookmarkedRequestIsXRequestedWithThenPostAuthenticationRedire .header("X-Requested-With", "XMLHttpRequest"); MockHttpSession session = (MockHttpSession) this.mvc .perform(xRequestedWith) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -157,7 +157,7 @@ public void getWhenBookmarkedRequestIsTextEventStreamThenPostAuthenticationRedir MediaType.TEXT_EVENT_STREAM); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -174,7 +174,7 @@ public void getWhenBookmarkedRequestIsAllMediaTypeThenPostAuthenticationRemember MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.ALL); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -188,7 +188,7 @@ public void getWhenBookmarkedRequestIsTextHtmlThenPostAuthenticationRemembers() MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -203,7 +203,7 @@ public void getWhenBookmarkedRequestIsChromeThenPostAuthenticationRemembers() th MockHttpServletRequestBuilder request = get("/messages") .header(HttpHeaders.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); @@ -218,7 +218,7 @@ public void getWhenBookmarkedRequestIsRequestedWithAndroidThenPostAuthentication MockHttpServletRequestBuilder request = get("/messages") .header("X-Requested-With", "com.android"); MockHttpSession session = (MockHttpSession) this.mvc.perform(request) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn() .getRequest() .getSession(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java index 65a56cdb7aa..b56d047a5f7 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java @@ -437,7 +437,7 @@ public void oauth2LoginWithOneClientConfiguredThenRedirectForAuthorization() thr this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("/oauth2/authorization/google"); + assertThat(this.response.getRedirectedUrl()).matches("http://localhost/oauth2/authorization/google"); } // gh-6802 @@ -448,7 +448,7 @@ public void oauth2LoginWithOneClientConfiguredAndFormLoginThenRedirectDefaultLog this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("/login"); + assertThat(this.response.getRedirectedUrl()).matches("http://localhost/login"); } // gh-5347 @@ -461,7 +461,7 @@ public void oauth2LoginWithOneClientConfiguredAndRequestFaviconNotAuthenticatedT this.request.setServletPath(requestUri); this.request.addHeader(HttpHeaders.ACCEPT, new MediaType("image", "*").toString()); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("/login"); + assertThat(this.response.getRedirectedUrl()).matches("http://localhost/login"); } // gh-5347 @@ -472,7 +472,7 @@ public void oauth2LoginWithMultipleClientsConfiguredThenRedirectDefaultLoginPage this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("/login"); + assertThat(this.response.getRedirectedUrl()).matches("http://localhost/login"); } // gh-6812 @@ -521,7 +521,7 @@ public void oauth2LoginWithOneAuthorizationCodeClientAndOtherClientsConfiguredTh this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("/oauth2/authorization/google"); + assertThat(this.response.getRedirectedUrl()).matches("http://localhost/oauth2/authorization/google"); } @Test @@ -531,7 +531,7 @@ public void oauth2LoginWithCustomLoginPageThenRedirectCustomLoginPage() throws E this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("/custom-login"); + assertThat(this.response.getRedirectedUrl()).matches("http://localhost/custom-login"); } @Test @@ -541,7 +541,7 @@ public void requestWhenOauth2LoginWithCustomLoginPageInLambdaThenRedirectCustomL this.request = new MockHttpServletRequest("GET", requestUri); this.request.setServletPath(requestUri); this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain); - assertThat(this.response.getRedirectedUrl()).matches("/custom-login"); + assertThat(this.response.getRedirectedUrl()).matches("http://localhost/custom-login"); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index 2dad6916fe6..c247a6d7fed 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -1210,7 +1210,7 @@ public void requestWhenFormLoginAndResourceServerEntryPointsThenSessionCreatedBy MvcResult result = this.mvc.perform(get("/authenticated") .header("Accept", "text/html")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn(); // @formatter:on assertThat(result.getRequest().getSession(false)).isNotNull(); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java index 3a912072908..6d874a583d6 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -412,11 +412,10 @@ public void getFaviconWhenDefaultConfigurationThenDoesNotSaveAuthnRequest() thro this.spring.register(Saml2LoginConfig.class).autowire(); this.mvc.perform(get("/favicon.ico").accept(MediaType.TEXT_HTML)) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); this.mvc.perform(get("/").accept(MediaType.TEXT_HTML)) .andExpect(status().isFound()) - .andExpect(header().string("Location", startsWith("/saml2/authenticate"))); - + .andExpect(header().string("Location", startsWith("http://localhost/saml2/authenticate"))); } @Test diff --git a/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java b/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java index 781cce82e92..901945e73aa 100644 --- a/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -342,7 +342,7 @@ public void postWhenHasCsrfTokenButSessionExpiresThenRequestIsCancelledAfterSucc this.spring.configLocations(this.xml("CsrfEnabled")).autowire(); // simulates a request that has no authentication (e.g. session time-out) MvcResult result = this.mvc.perform(post("/authenticated").with(csrf())) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("http://localhost/login")) .andReturn(); MockHttpSession session = (MockHttpSession) result.getRequest().getSession(); // if the request cache is consulted, then it will redirect back to /some-url, @@ -363,7 +363,9 @@ public void getWhenHasCsrfTokenButSessionExpiresThenRequestIsRememeberedAfterSuc throws Exception { this.spring.configLocations(this.xml("CsrfEnabled")).autowire(); // simulates a request that has no authentication (e.g. session time-out) - MvcResult result = this.mvc.perform(get("/authenticated")).andExpect(redirectedUrl("/login")).andReturn(); + MvcResult result = this.mvc.perform(get("/authenticated")) + .andExpect(redirectedUrl("http://localhost/login")) + .andReturn(); MockHttpSession session = (MockHttpSession) result.getRequest().getSession(); // if the request cache is consulted, then it will redirect back to /some-url, // which we do want diff --git a/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java b/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java index b054762aac3..52237273df3 100644 --- a/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * 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. @@ -73,7 +73,7 @@ public void getProtectedPageWhenFormLoginConfiguredThenRedirectsToDefaultLoginPa this.spring.configLocations(this.xml("WithAntRequestMatcher")).autowire(); // @formatter:off this.mvc.perform(get("/")) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -107,7 +107,7 @@ public void authenticateWhenConfiguredWithSpelThenRedirectsAccordingly() throws this.mvc.perform(invalidPassword) .andExpect(redirectedUrl(WebConfigUtilsTests.URL + "/failure")); this.mvc.perform(get("/")) - .andExpect(redirectedUrl(WebConfigUtilsTests.URL + "/login")); + .andExpect(redirectedUrl("http://localhost" + WebConfigUtilsTests.URL + "/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java index 62b4ab99dd1..c7f0590bc1b 100644 --- a/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -71,7 +71,7 @@ public void getWhenUsingMinimalConfigurationThenRedirectsToLogin() throws Except // @formatter:off this.mvc.perform(get("/")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -81,7 +81,7 @@ public void getWhenUsingMinimalAuthorizationManagerThenRedirectsToLogin() throws // @formatter:off this.mvc.perform(get("/")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -95,7 +95,7 @@ public void getWhenUsingAuthorizationManagerThenRedirectsToLogin() throws Except // @formatter:off this.mvc.perform(get("/")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on verify(authorizationManager).check(any(), any()); } @@ -109,7 +109,7 @@ public void getWhenUsingMinimalConfigurationThenPreventsSessionAsUrlParameter() proxy.doFilter(request, new EncodeUrlDenyingHttpServletResponseWrapper(response), (req, resp) -> { }); assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY); - assertThat(response.getRedirectedUrl()).isEqualTo("/login"); + assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login"); } @Test diff --git a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java index b08a3c868a0..180bd2ec532 100644 --- a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -573,7 +573,7 @@ public void configureWhenUsingDisableUrlRewritingThenRedirectIsNotEncodedByRespo proxy.doFilter(request, new EncodeUrlDenyingHttpServletResponseWrapper(response), (req, resp) -> { }); assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_MOVED_TEMPORARILY); - assertThat(response.getRedirectedUrl()).isEqualTo("/login"); + assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login"); } @Test @@ -802,7 +802,7 @@ public void authenticateWhenUsingPortMapperThenRedirectsAppropriately() throws E this.spring.configLocations(xml("PortsMappedRequiresHttps")).autowire(); // @formatter:off MockHttpSession session = (MockHttpSession) this.mvc.perform(get("https://localhost:9080/protected")) - .andExpect(redirectedUrl("/login")) + .andExpect(redirectedUrl("https://localhost:9443/login")) .andReturn() .getRequest() .getSession(false); diff --git a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java index 6c1e24dcee9..b632f834de2 100644 --- a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -174,7 +174,7 @@ public void requestWhenSingleClientRegistrationThenAutoRedirect() throws Excepti // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/oauth2/authorization/google-login")); + .andExpect(redirectedUrl("http://localhost/oauth2/authorization/google-login")); // @formatter:on verify(this.requestCache).saveRequest(any(), any()); } @@ -187,7 +187,7 @@ public void requestWhenSingleClientRegistrationAndRequestFaviconNotAuthenticated // @formatter:off this.mvc.perform(get("/favicon.ico").accept(new MediaType("image", "*"))) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -199,7 +199,7 @@ public void requestWhenSingleClientRegistrationAndRequestXHRNotAuthenticatedThen // @formatter:off this.mvc.perform(get("/").header("X-Requested-With", "XMLHttpRequest")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -411,7 +411,7 @@ public void requestWhenMultiClientRegistrationThenRedirectDefaultLoginPage() thr // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -421,7 +421,7 @@ public void requestWhenCustomLoginPageThenRedirectCustomLoginPage() throws Excep // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/custom-login")); + .andExpect(redirectedUrl("http://localhost/custom-login")); // @formatter:on } @@ -433,7 +433,7 @@ public void requestWhenSingleClientRegistrationAndFormLoginConfiguredThenRedirec // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java b/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java index 0c1710f98b9..ffe686efc16 100644 --- a/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/PlaceHolderAndELConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * 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. @@ -73,7 +73,7 @@ public void loginWhenUsingPlaceholderThenInterceptUrlsAndFormLoginWorks() throws // login-page setting // @formatter:off this.mvc.perform(get("/secured")) - .andExpect(redirectedUrl("/loginPage")); + .andExpect(redirectedUrl("http://localhost/loginPage")); // login-processing-url setting // default-target-url setting this.mvc.perform(post("/loginPage").param("username", "user").param("password", "password")) @@ -98,7 +98,7 @@ public void loginWhenUsingSpELThenInterceptUrlsAndFormLoginWorks() throws Except // login-page setting // @formatter:off this.mvc.perform(get("/secured")) - .andExpect(redirectedUrl("/loginPage")); + .andExpect(redirectedUrl("http://localhost/loginPage")); // login-processing-url setting // default-target-url setting this.mvc.perform(post("/loginPage").param("username", "user").param("password", "password")) diff --git a/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java index fad02a9ae0c..9eb168233ab 100644 --- a/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -161,7 +161,7 @@ public void requestWhenSingleRelyingPartyRegistrationThenAutoRedirect() throws E // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/saml2/authenticate/one")); + .andExpect(redirectedUrl("http://localhost/saml2/authenticate/one")); // @formatter:on verify(this.requestCache).saveRequest(any(), any()); } @@ -172,7 +172,7 @@ public void requestWhenMultiRelyingPartyRegistrationThenRedirectToLoginWithRelyi // @formatter:off this.mvc.perform(get("/")) .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java b/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java index 5a7d0a90382..713a03b846b 100644 --- a/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/SecurityContextHolderAwareRequestConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * 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. @@ -78,7 +78,7 @@ public void servletAuthenticateWhenUsingDefaultConfigurationThenUsesSpringSecuri // @formatter:off this.mvc.perform(get("/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -114,7 +114,7 @@ public void servletAuthenticateWhenUsingFormLoginThenUsesSpringSecurity() throws // @formatter:off this.mvc.perform(get("/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); // @formatter:on } @@ -137,10 +137,10 @@ public void servletAuthenticateWhenUsingMultipleHttpConfigsThenUsesSpringSecurit // @formatter:off this.mvc.perform(get("/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login")); + .andExpect(redirectedUrl("http://localhost/login")); this.mvc.perform(get("/v2/authenticate")) .andExpect(status().isFound()) - .andExpect(redirectedUrl("/login2")); + .andExpect(redirectedUrl("http://localhost/login2")); // @formatter:on } @@ -177,10 +177,10 @@ public void servletLogoutWhenUsingMultipleHttpConfigsThenUsesSpringSecurity() th @Test public void servletLogoutWhenUsingCustomLogoutThenUsesSpringSecurity() throws Exception { this.spring.configLocations(this.xml("Logout")).autowire(); - // @formatter:off this.mvc.perform(get("/authenticate")) - .andExpect(status().isFound()) - .andExpect(redirectedUrl("/signin")); + .andExpect(status().isFound()) + .andExpect(redirectedUrl("http://localhost/signin")); + // @formatter:off MvcResult result = this.mvc.perform(get("/good-login")) .andReturn(); // @formatter:on diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt index 2aea44c6ffb..ed3e409cfff 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/ExceptionHandlingDslTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -217,7 +217,7 @@ class ExceptionHandlingDslTests { this.mockMvc.get("/") .andExpect { status { isFound() } - redirectedUrl("/custom-login") + redirectedUrl("http://localhost/custom-login") } } @@ -246,13 +246,13 @@ class ExceptionHandlingDslTests { this.mockMvc.get("/secured1") .andExpect { status { isFound() } - redirectedUrl("/custom-login1") + redirectedUrl("http://localhost/custom-login1") } this.mockMvc.get("/secured2") .andExpect { status { isFound() } - redirectedUrl("/custom-login2") + redirectedUrl("http://localhost/custom-login2") } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt index 5b00105247c..965c361b4a3 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/FormLoginDslTests.kt @@ -143,7 +143,7 @@ class FormLoginDslTests { this.mockMvc.get("/") .andExpect { status { isFound() } - redirectedUrl("/login") + redirectedUrl("http://localhost/login") } } @@ -169,7 +169,7 @@ class FormLoginDslTests { this.mockMvc.get("/") .andExpect { status { isFound() } - redirectedUrl("/log-in") + redirectedUrl("http://localhost/log-in") } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt index a3a07cc0106..b73b41f50de 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/RememberMeDslTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 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. @@ -153,7 +153,7 @@ internal class RememberMeDslTests { cookie(expiredRememberMeCookie) }.andExpect { status { isFound() } - redirectedUrl("/login") + redirectedUrl("http://localhost/login") } } @@ -229,7 +229,7 @@ internal class RememberMeDslTests { cookie(withoutKeyRememberMeCookie) }.andExpect { status { isFound() } - redirectedUrl("/login") + redirectedUrl("http://localhost/login") } val keyMvcResult = mockMvc.post("/login") { loginRememberMeRequest() diff --git a/docs/modules/ROOT/pages/migration-7/web.adoc b/docs/modules/ROOT/pages/migration-7/web.adoc new file mode 100644 index 00000000000..024d5604494 --- /dev/null +++ b/docs/modules/ROOT/pages/migration-7/web.adoc @@ -0,0 +1,104 @@ += Web Migrations + +== Favor Relative URIs + +When redirecting to a login endpoint, Spring Security has favored absolute URIs in the past. +For example, if you set your login page like so: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +http + // ... + .formLogin((form) -> form.loginPage("/my-login")) + // ... +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +http { + formLogin { + loginPage = "/my-login" + } +} +---- + +Xml:: ++ +[source,kotlin,role="secondary"] +---- + + + +---- +====== + +then when redirecting to `/my-login` Spring Security would use a `Location:` like the following: + +[source] +---- +302 Found +// ... +Location: https://myapp.example.org/my-login +---- + +However, this is no longer necessary given that the RFC is was based on is now obsolete. + +In Spring Security 7, this is changed to use a relative URI like so: + +[source] +---- +302 Found +// ... +Location: /my-login +---- + +Most applications will not notice a difference. +However, in the event that this change causes problems, you can switch back to the Spring Security 6 behavior by setting the `favorRelativeUrls` value: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +LoginUrlAuthenticationEntryPoint entryPoint = new LoginUrlAuthenticationEntryPoint("/my-login"); +entryPoint.setFavorRelativeUris(false); +http + // ... + .exceptionHandling((exceptions) -> exceptions.authenticaitonEntryPoint(entryPoint)) + // ... +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +LoginUrlAuthenticationEntryPoint entryPoint = LoginUrlAuthenticationEntryPoint("/my-login") +entryPoint.setFavorRelativeUris(false) + +http { + exceptionHandling { + authenticationEntryPoint = entryPoint + } +} +---- + +Xml:: ++ +[source,kotlin,role="secondary"] +---- + + + + + + + +---- +====== diff --git a/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java b/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java index 60cf4eef4c1..5e62d2ebeb6 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java +++ b/web/src/main/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPoint.java @@ -78,6 +78,8 @@ public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoin private boolean useForward = false; + private boolean favorRelativeUris = false; + private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); /** @@ -144,23 +146,41 @@ public void commence(HttpServletRequest request, HttpServletResponse response, protected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { String loginForm = determineUrlToUseForThisRequest(request, response, authException); - if (UrlUtils.isAbsoluteUrl(loginForm) || !this.forceHttps || "https".equals(request.getScheme())) { + if (UrlUtils.isAbsoluteUrl(loginForm)) { return loginForm; } + if (requiresRewrite(request)) { + return httpsUri(request, loginForm); + } + return this.favorRelativeUris ? loginForm : absoluteUri(request, loginForm).getUrl(); + } + + private boolean requiresRewrite(HttpServletRequest request) { + return this.forceHttps && "http".equals(request.getScheme()); + } + + private String httpsUri(HttpServletRequest request, String path) { int serverPort = this.portResolver.getServerPort(request); Integer httpsPort = this.portMapper.lookupHttpsPort(serverPort); if (httpsPort == null) { logger.warn(LogMessage.format("Unable to redirect to HTTPS as no port mapping found for HTTP port %s", serverPort)); - return loginForm; + return this.favorRelativeUris ? path : absoluteUri(request, path).getUrl(); } + RedirectUrlBuilder builder = absoluteUri(request, path); + builder.setScheme("https"); + builder.setPort(httpsPort); + return builder.getUrl(); + } + + private RedirectUrlBuilder absoluteUri(HttpServletRequest request, String path) { RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder(); - urlBuilder.setScheme("https"); + urlBuilder.setScheme(request.getScheme()); urlBuilder.setServerName(request.getServerName()); - urlBuilder.setPort(httpsPort); + urlBuilder.setPort(this.portResolver.getServerPort(request)); urlBuilder.setContextPath(request.getContextPath()); - urlBuilder.setPathInfo(loginForm); - return urlBuilder.getUrl(); + urlBuilder.setPathInfo(path); + return urlBuilder; } /** @@ -238,4 +258,18 @@ protected boolean isUseForward() { return this.useForward; } + /** + * Favor using relative URIs when formulating a redirect. + * + *

+ * Note that a relative redirect is not always possible. For example, when redirecting + * from {@code http} to {@code https}, the URL needs to be absolute. + *

+ * @param favorRelativeUris whether to favor relative URIs or not + * @since 6.5 + */ + public void setFavorRelativeUris(boolean favorRelativeUris) { + this.favorRelativeUris = favorRelativeUris; + } + } diff --git a/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java b/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java index ad699da8ea9..91e2d93cdfa 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/LoginUrlAuthenticationEntryPointTests.java @@ -129,18 +129,18 @@ public void testHttpsOperationFromOriginalHttpsUrl() throws Exception { ep.setPortResolver(new MockPortResolver(80, 443)); ep.afterPropertiesSet(); ep.commence(request, response, null); - assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("https://www.example.com/bigWebApp/hello"); request.setServerPort(8443); response = new MockHttpServletResponse(); ep.setPortResolver(new MockPortResolver(8080, 8443)); ep.commence(request, response, null); - assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("https://www.example.com:8443/bigWebApp/hello"); // access to https via http port request.setServerPort(8080); response = new MockHttpServletResponse(); ep.setPortResolver(new MockPortResolver(8080, 8443)); ep.commence(request, response, null); - assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("https://www.example.com:8443/bigWebApp/hello"); } @Test @@ -158,7 +158,7 @@ public void testNormalOperation() throws Exception { request.setServerPort(80); MockHttpServletResponse response = new MockHttpServletResponse(); ep.commence(request, response, null); - assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/bigWebApp/hello"); } @Test @@ -178,7 +178,7 @@ public void testOperationWhenHttpsRequestsButHttpsPortUnknown() throws Exception ep.commence(request, response, null); // Response doesn't switch to HTTPS, as we didn't know HTTP port 8888 to HTTP port // mapping - assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost:8888/bigWebApp/hello"); } @Test @@ -237,4 +237,54 @@ public void absoluteLoginFormUrlCantBeUsedWithForwarding() throws Exception { assertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet); } + @Test + public void commenceWhenFavorRelativeUrisThenHttpsSchemeNotIncluded() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/some_path"); + request.setScheme("https"); + request.setServerName("www.example.com"); + request.setContextPath("/bigWebApp"); + request.setServerPort(443); + MockHttpServletResponse response = new MockHttpServletResponse(); + LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint("/hello"); + ep.setFavorRelativeUris(true); + ep.setPortMapper(new PortMapperImpl()); + ep.setForceHttps(true); + ep.setPortMapper(new PortMapperImpl()); + ep.setPortResolver(new MockPortResolver(80, 443)); + ep.afterPropertiesSet(); + ep.commence(request, response, null); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + request.setServerPort(8443); + response = new MockHttpServletResponse(); + ep.setPortResolver(new MockPortResolver(8080, 8443)); + ep.commence(request, response, null); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + // access to https via http port + request.setServerPort(8080); + response = new MockHttpServletResponse(); + ep.setPortResolver(new MockPortResolver(8080, 8443)); + ep.commence(request, response, null); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + } + + @Test + public void commenceWhenFavorRelativeUrisThenHttpSchemeNotIncluded() throws Exception { + LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint("/hello"); + ep.setFavorRelativeUris(true); + ep.setPortMapper(new PortMapperImpl()); + ep.setPortResolver(new MockPortResolver(80, 443)); + ep.afterPropertiesSet(); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/some_path"); + request.setContextPath("/bigWebApp"); + request.setScheme("http"); + request.setServerName("localhost"); + request.setContextPath("/bigWebApp"); + request.setServerPort(80); + MockHttpServletResponse response = new MockHttpServletResponse(); + ep.commence(request, response, null); + assertThat(response.getRedirectedUrl()).isEqualTo("/bigWebApp/hello"); + } + }