Expected Behavior
http
.redirectStrategy(ForwardHeadersRedirectStrategy())
Current Behavior
I basically have to recreate the code to rebuild the default login screen
val reactiveAuthenticationManager = UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService)
val requestCache = WebSessionServerRequestCache()
val forwardHeadersRedirectStrategy = ForwardHeadersRedirectStrategy()
val authenticationEntryPoint =
ForwardHeadersAuthenticationEntryPoint("/login", requestCache)
val authenticationSuccessHandler = RedirectServerAuthenticationSuccessHandler("/")
authenticationSuccessHandler.setRedirectStrategy(forwardHeadersRedirectStrategy)
authenticationSuccessHandler.setRequestCache(requestCache)
val authenticationFailureHandler = RedirectServerAuthenticationFailureHandler("/login?error")
authenticationFailureHandler.setRedirectStrategy(forwardHeadersRedirectStrategy)
val loginPageGeneratingWebFilter = LoginPageGeneratingWebFilter()
loginPageGeneratingWebFilter.setFormLoginEnabled(true)
loginPageGeneratingWebFilter.setOauth2AuthenticationUrlToClientName()
return http
.addFilterAt(loginPageGeneratingWebFilter, SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING)
.addFilterAt(LogoutPageGeneratingWebFilter(), SecurityWebFiltersOrder.LOGOUT_PAGE_GENERATING)
...
.formLogin {
it.loginPage("/login")
it.authenticationFailureHandler(authenticationFailureHandler)
it.authenticationSuccessHandler(authenticationSuccessHandler)
it.authenticationEntryPoint(authenticationEntryPoint)
}
Context
I get X-Forwarded-For headers which are not recognized by Spring Security and such it will redirect without the host name.
class ForwardHeadersRedirectStrategy : ServerRedirectStrategy {
override fun sendRedirect(exchange: ServerWebExchange, location: URI): Mono<Void> =
Mono.fromRunnable {
val response = exchange.response
response.setStatusCode(HttpStatus.SEE_OTHER)
val newLocation: URI = createLocation(exchange, location)
response.headers.location = newLocation
}
private fun createLocation(exchange: ServerWebExchange, location: URI): URI {
val url = location.toASCIIString()
if (url.startsWith("/") && exchange.request.headers["x-forwarded-proto"] != null) {
val context = exchange.request.path.contextPath().value()
val forwardedProto = exchange.request.headers["x-forwarded-proto"]!![0]
var forwardedPort = exchange.request.headers["x-forwarded-port"]!![0]
when {
"https".equals(forwardedProto) && "443".equals(forwardedPort) -> {
forwardedPort = null;
}
"http".equals(forwardedProto) && "80".equals(forwardedPort) -> {
forwardedPort = null;
}
}
return UriComponentsBuilder.newInstance()
.scheme(forwardedProto)
.host(exchange.request.headers["x-forwarded-host"]!![0])
.port(forwardedPort)
.path(context)
.path(location.path)
.query(location.query)
.build()
.toUri()
}
return location
}
}
Comment From: marcusdacoregio
Hi, @trajano.
I don't think I follow, why you cannot just customize the ServerAuthenticationSuccessHandler or ServerAuthenticationFailureHandler as mentioned here?
Comment From: trajano
Let me try it out. And to answer your question ... because I didn't see it documented as an option. But looking at the solution
This will override authenticationSuccessHandler so I guess I would also need to change the failure as well, but it's missing authenticationEntryPoint BUT because of
private final class LoginPageSpec {
protected void configure(ServerHttpSecurity http) {
if (http.authenticationEntryPoint != null) {
return;
}
It destroys the default login forms. So it brings back to my problem where I had to recreate the specs to bring back the original login form capability.
Comment From: marcusdacoregio
If you override the authenticationEntryPoint, Spring Security assumes that you do not want the default behavior and back-off on generating the default login page. Ideally, the default login page should be used only to give users a starting point for form login and should be replaced with a custom login page.
Comment From: trajano
That's correct, but I didn't really want to change the entry point either, I just wanted to change the redirect strategy. But there was no facility to just change that, instead I had to recreate all the components down to the authenticationEntryPoint. As per my OP I just wanted
http.redirectStrategy(customRedirectStrategy)
Or to follow the rest of the convention would be something like
http.authenticationEntryPoint { it.setRedirectStrategy(customRedirectStrategy) }
http.authenticationSuccessHandler { it.setRedirectStrategy(customRedirectStrategy) }
http.authenticationFailureHandler { it.setRedirectStrategy(customRedirectStrategy) }
Another approach is to have that logic I wrote to handle X-Forward... headers be handled by the default redirect strategy.
Comment From: marcusdacoregio
I am not sure if we want those global strategies since they could become too complex to handle the configuration scenarios in different authentication mechanisms. Furthermore, it seems that you want to handle absolute URIs in redirection, which has been covered by https://github.com/spring-projects/spring-security/issues/11656.
That being said, I'll close this as declined since we are not looking into adding that for now.