I have such a test
@Test
@WithAnonymousUser
void givenNoAuthentication_whenEnteringMainPage_thenRedirectToLoginPage() throws Exception {
mockMvc.perform(get("/")).andExpect(redirectedUrl("/login"));
}
And I got assertion error like this:
java.lang.AssertionError: Redirected URL expected:</login> but was:<http://localhost/login>
Expected :/login
Actual :http://localhost/login
What is more, I have another test similar to this one which tests if logged in user gets redirected to main page ("/") after accessing /login and this test is working fine
@Test
@WithMockUser
void givenAuthentication_whenEnteringLoginPage_thenRedirectToMainPage() throws Exception {
mockMvc.perform(get("/login")).andExpect(redirectedUrl("/"));
}
Here is how my spring security config looks like
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
.authorizeHttpRequests(authorize - > authorize
.requestMatchers("/css/**").permitAll()
.requestMatchers("/js/**").permitAll()
.requestMatchers("/login").permitAll()
.requestMatchers("/register").permitAll()
.requestMatchers("/user/register").permitAll()
.anyRequest().authenticated()
)
.formLogin(form - > form
.loginPage("/login")
.defaultSuccessUrl("/", true)
)
.logout(logout - > logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
)
.build();
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
How can I fix my first test, and why it doesnt't work when second test which is almost the same is working fine?
EDIT:
I managed to fix this by using .endsWith("/login")
like so
MvcResult result = mockMvc.perform(get("/")).andReturn();
assertThat(result.getResponse().getRedirectedUrl()).endsWith("/login");
But I still don't understand why in this test redirect url contains hostname, and it other it doesn't
While not a definitive answer, maybe my research will help someone as this behavior is a bit annoying when writing tests for /login
, because that endpoint behaves differently.
There are some related answers:
I believe the behavior is intentional for two reasons
/login
and no other endpoints. org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint.buildRedirectUrlToLoginPage
is responsible for this behavior, there is explicit logic that makes the URL absolute (spring.security.web:6.2.1
):if (UrlUtils.isAbsoluteUrl(loginForm)) {
return loginForm;
}
// ...
// otherwise construct absolute URL
redirectedUrl
for login set explicitly to test absolute values everywhere. On all other places the tests uses relative paths.Maybe it's a bug or was written in that way due to historical reasons - e.g. older RFC 2616 stated to use absolute uris:
Location = "Location" ":" absoluteURI
but the newer RFC 7231 allows relative paths (details in this answer).
Or maybe there is some security concern in mind - forcing to use absolute URI on login so that app have "more" under control whether http
or https
is used.
Either way, I haven't found a way to configure it to behave differently. So right now I'm forced to test /login
endpoints differently than other endpoints.