浏览代码

Add support customizing redirect URI

Closes gh-14778
Max Batischev 1 年之前
父节点
当前提交
ed3f3d17b2

+ 84 - 10
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/web/server/logout/OidcClientInitiatedServerLogoutSuccessHandler.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.
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import reactor.core.publisher.Mono;
 
+import org.springframework.core.convert.converter.Converter;
 import org.springframework.http.server.reactive.ServerHttpRequest;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
@@ -35,6 +36,7 @@ import org.springframework.security.web.server.WebFilterExchange;
 import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler;
 import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
 import org.springframework.util.Assert;
+import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.util.UriComponents;
 import org.springframework.web.util.UriComponentsBuilder;
 
@@ -57,6 +59,8 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
 
 	private String postLogoutRedirectUri;
 
+	private Converter<RedirectUriParameters, Mono<String>> redirectUriResolver = new DefaultRedirectUriResolver();
+
 	/**
 	 * Constructs an {@link OidcClientInitiatedServerLogoutSuccessHandler} with the
 	 * provided parameters
@@ -79,15 +83,10 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
 				.map(OAuth2AuthenticationToken.class::cast)
 				.map(OAuth2AuthenticationToken::getAuthorizedClientRegistrationId)
 				.flatMap(this.clientRegistrationRepository::findByRegistrationId)
-				.flatMap((clientRegistration) -> {
-					URI endSessionEndpoint = endSessionEndpoint(clientRegistration);
-					if (endSessionEndpoint == null) {
-						return Mono.empty();
-					}
-					String idToken = idToken(authentication);
-					String postLogoutRedirectUri = postLogoutRedirectUri(exchange.getExchange().getRequest(), clientRegistration);
-					return Mono.just(endpointUri(endSessionEndpoint, idToken, postLogoutRedirectUri));
-				})
+				.flatMap((clientRegistration) ->
+					this.redirectUriResolver.convert(
+							new RedirectUriParameters(exchange.getExchange(), authentication, clientRegistration))
+				)
 				.switchIfEmpty(
 						this.serverLogoutSuccessHandler.onLogoutSuccess(exchange, authentication).then(Mono.empty())
 				)
@@ -189,4 +188,79 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
 		this.serverLogoutSuccessHandler.setLogoutSuccessUrl(logoutSuccessUrl);
 	}
 
+	/**
+	 * Set the {@link Converter} that converts {@link RedirectUriParameters} to redirect
+	 * URI
+	 * @param redirectUriResolver {@link Converter}
+	 * @since 6.5
+	 */
+	public void setRedirectUriResolver(Converter<RedirectUriParameters, Mono<String>> redirectUriResolver) {
+		Assert.notNull(redirectUriResolver, "redirectUriResolver cannot be null");
+		this.redirectUriResolver = redirectUriResolver;
+	}
+
+	/**
+	 * Parameters, required for redirect URI resolving.
+	 *
+	 * @author Max Batischev
+	 * @since 6.5
+	 */
+	public static final class RedirectUriParameters {
+
+		private final ServerWebExchange serverWebExchange;
+
+		private final Authentication authentication;
+
+		private final ClientRegistration clientRegistration;
+
+		public RedirectUriParameters(ServerWebExchange serverWebExchange, Authentication authentication,
+				ClientRegistration clientRegistration) {
+			Assert.notNull(clientRegistration, "clientRegistration cannot be null");
+			Assert.notNull(serverWebExchange, "serverWebExchange cannot be null");
+			Assert.notNull(authentication, "authentication cannot be null");
+			this.serverWebExchange = serverWebExchange;
+			this.authentication = authentication;
+			this.clientRegistration = clientRegistration;
+		}
+
+		public ServerWebExchange getServerWebExchange() {
+			return this.serverWebExchange;
+		}
+
+		public Authentication getAuthentication() {
+			return this.authentication;
+		}
+
+		public ClientRegistration getClientRegistration() {
+			return this.clientRegistration;
+		}
+
+	}
+
+	/**
+	 * Default {@link Converter} for redirect uri resolving.
+	 *
+	 * @since 6.5
+	 */
+	private final class DefaultRedirectUriResolver implements Converter<RedirectUriParameters, Mono<String>> {
+
+		@Override
+		public Mono<String> convert(RedirectUriParameters redirectUriParameters) {
+			// @formatter:off
+			return Mono.just(redirectUriParameters.authentication)
+				.flatMap((authentication) -> {
+					URI endSessionEndpoint = endSessionEndpoint(redirectUriParameters.clientRegistration);
+					if (endSessionEndpoint == null) {
+						return Mono.empty();
+					}
+					String idToken = idToken(authentication);
+					String postLogoutRedirectUri = postLogoutRedirectUri(
+							redirectUriParameters.serverWebExchange.getRequest(), redirectUriParameters.clientRegistration);
+					return Mono.just(endpointUri(endSessionEndpoint, idToken, postLogoutRedirectUri));
+				});
+			// @formatter:on
+		}
+
+	}
+
 }

+ 21 - 1
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/web/server/logout/OidcClientInitiatedServerLogoutSuccessHandlerTests.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.
@@ -19,6 +19,7 @@ package org.springframework.security.oauth2.client.oidc.web.server.logout;
 import java.io.IOException;
 import java.net.URI;
 import java.util.Collections;
+import java.util.Objects;
 
 import jakarta.servlet.ServletException;
 import org.junit.jupiter.api.BeforeEach;
@@ -199,6 +200,25 @@ public class OidcClientInitiatedServerLogoutSuccessHandlerTests {
 		assertThatIllegalArgumentException().isThrownBy(() -> this.handler.setPostLogoutRedirectUri((String) null));
 	}
 
+	@Test
+	public void logoutWhenCustomRedirectUriResolverSetThenRedirects() {
+		OAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),
+				AuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());
+		WebFilterExchange filterExchange = new WebFilterExchange(this.exchange, this.chain);
+		given(this.exchange.getRequest())
+			.willReturn(MockServerHttpRequest.get("/").queryParam("location", "https://test.com").build());
+		// @formatter:off
+		this.handler.setRedirectUriResolver((params) -> Mono.just(
+						Objects.requireNonNull(params.getServerWebExchange()
+								.getRequest()
+								.getQueryParams()
+								.getFirst("location"))));
+		// @formatter:on
+		this.handler.onLogoutSuccess(filterExchange, token).block();
+
+		assertThat(redirectedUrl(this.exchange)).isEqualTo("https://test.com");
+	}
+
 	private String redirectedUrl(ServerWebExchange exchange) {
 		return exchange.getResponse().getHeaders().getFirst("Location");
 	}