浏览代码

Use original query string to verify signature

Closes gh-11235
Josh Cummings 3 年之前
父节点
当前提交
5cbc1a47da

+ 52 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java

@@ -22,6 +22,9 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -267,6 +270,33 @@ public class Saml2LogoutConfigurerTests {
 		verify(getBean(LogoutHandler.class)).logout(any(), any(), any());
 	}
 
+	// gh-11235
+	@Test
+	public void saml2LogoutRequestWhenLowercaseEncodingThenLogsOutAndSendsLogoutResponse() throws Exception {
+		this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
+		String apLogoutRequest = "nZFNa4QwEIb/iuQeP6K7dYO6FKQg2B622x56G3WwgiY2E8v239fqCksPPfSWIXmfNw+THC9D73yi\r\n"
+				+ "oU6rlAWuzxxUtW461abs5fzAY3bMEoKhF6Msdasne8KPCck6c1KRXK9SNhklNVBHUsGAJG0tn+8f\r\n"
+				+ "SylcX45GW13rnjn5HOwU2KXt3dqRpOeZ0cULDGOPrjat1y8t3gL2zFrGnCJPWXkKcR8KCHY8xmrP\r\n"
+				+ "Iz868OpOVLwO4wohggagmd8STVgosqBsyoQvBPd3XITnIJaRL8PYjcThjTmvm/f8SXa1lEvY3Nr9\r\n"
+				+ "LQdEaH6EWAYjR2U7+8W7JvFucRv8aY4X+b/g03zaoCsmu46/FpN9Aw==";
+		String apLogoutRequestRelayState = "d118dbd5-3853-4268-b3e5-c40fc033fa2f";
+		String apLogoutRequestSignature = "VZ7rWa5u3hIX60fAQs/gBQZWDP2BAIlCMMrNrTHafoKKj0uXWnuITYLuL8NdsWmyQN0+fqWW4X05+BqiLpL80jHLmQR5RVqqL1EtVv1SpPUna938lgz2sOliuYmfQNj4Bmd+Z5G1K6QhbVrtfb7TQHURjUafzfRm8+jGz3dPjVBrn/rD/umfGoSn6RuWngugcMNL4U0A+JcEh1NSfSYNVz7y+MqlW1UhX2kF86rm97ERCrxay7Gh/bI2f3fJPJ1r+EyLjzrDUkqw5cva3rVlFgEQouMVu35lUJn7SFompW8oTxkI23oc/t+AGZqaBupNITNdjyGCBpfukZ69EZrj8g==";
+		DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user",
+				Collections.emptyMap());
+		principal.setRelyingPartyRegistrationId("get");
+		Saml2Authentication user = new Saml2Authentication(principal, "response",
+				AuthorityUtils.createAuthorityList("ROLE_USER"));
+		MvcResult result = this.mvc
+				.perform(get("/logout/saml2/slo").param("SAMLRequest", apLogoutRequest)
+						.param("RelayState", apLogoutRequestRelayState).param("SigAlg", this.apLogoutRequestSigAlg)
+						.param("Signature", apLogoutRequestSignature)
+						.with(new SamlQueryStringRequestPostProcessor(true)).with(authentication(user)))
+				.andExpect(status().isFound()).andReturn();
+		String location = result.getResponse().getHeader("Location");
+		assertThat(location).startsWith("https://ap.example.org/logout/saml2/response");
+		verify(getBean(LogoutHandler.class)).logout(any(), any(), any());
+	}
+
 	@Test
 	public void saml2LogoutRequestWhenNoRegistrationThen400() throws Exception {
 		this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
@@ -612,6 +642,26 @@ public class Saml2LogoutConfigurerTests {
 
 	static class SamlQueryStringRequestPostProcessor implements RequestPostProcessor {
 
+		private Function<String, String> urlEncodingPostProcessor = Function.identity();
+
+		SamlQueryStringRequestPostProcessor() {
+			this(false);
+		}
+
+		SamlQueryStringRequestPostProcessor(boolean lowercased) {
+			if (lowercased) {
+				Pattern encoding = Pattern.compile("%\\d[A-Fa-f]");
+				this.urlEncodingPostProcessor = (encoded) -> {
+					Matcher m = encoding.matcher(encoded);
+					while (m.find()) {
+						String found = m.group(0);
+						encoded = encoded.replace(found, found.toLowerCase());
+					}
+					return encoded;
+				};
+			}
+		}
+
 		@Override
 		public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
 			UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
@@ -619,7 +669,8 @@ public class Saml2LogoutConfigurerTests {
 				builder.queryParam(entries.getKey(),
 						UriUtils.encode(entries.getValue()[0], StandardCharsets.ISO_8859_1));
 			}
-			request.setQueryString(builder.build(true).toUriString().substring(1));
+			String queryString = this.urlEncodingPostProcessor.apply(builder.build(true).toUriString().substring(1));
+			request.setQueryString(queryString);
 			return request;
 		}
 

+ 5 - 0
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/jackson2/Saml2LogoutRequestMixin.java

@@ -17,9 +17,11 @@
 package org.springframework.security.saml2.jackson2;
 
 import java.util.Map;
+import java.util.function.Function;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
@@ -46,6 +48,9 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes
 @JsonIgnoreProperties(ignoreUnknown = true)
 class Saml2LogoutRequestMixin {
 
+	@JsonIgnore
+	Function<Map<String, String>, String> encoder;
+
 	@JsonCreator
 	Saml2LogoutRequestMixin(@JsonProperty("location") String location,
 			@JsonProperty("relayState") Saml2MessageBinding relayState,

+ 8 - 20
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlVerificationUtils.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 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.
@@ -50,7 +50,7 @@ import org.springframework.security.saml2.core.Saml2ErrorCodes;
 import org.springframework.security.saml2.core.Saml2ParameterNames;
 import org.springframework.security.saml2.core.Saml2X509Credential;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
-import org.springframework.web.util.UriUtils;
+import org.springframework.web.util.UriComponentsBuilder;
 
 /**
  * Utility methods for verifying SAML component signatures with OpenSAML
@@ -191,8 +191,9 @@ final class OpenSamlVerificationUtils {
 				else {
 					this.signature = null;
 				}
-				this.content = content(request.getSamlRequest(), Saml2ParameterNames.SAML_REQUEST,
-						request.getRelayState(), request.getParameter(Saml2ParameterNames.SIG_ALG));
+				this.content = UriComponentsBuilder.newInstance().query(request.getParametersQuery())
+						.replaceQueryParam(Saml2ParameterNames.SIGNATURE).build(true).toUriString().substring(1)
+						.getBytes(StandardCharsets.UTF_8);
 			}
 
 			RedirectSignature(Saml2LogoutResponse response) {
@@ -203,22 +204,9 @@ final class OpenSamlVerificationUtils {
 				else {
 					this.signature = null;
 				}
-				this.content = content(response.getSamlResponse(), Saml2ParameterNames.SAML_RESPONSE,
-						response.getRelayState(), response.getParameter(Saml2ParameterNames.SIG_ALG));
-			}
-
-			static byte[] content(String samlObject, String objectParameterName, String relayState, String algorithm) {
-				if (relayState != null) {
-					return String.format("%s=%s&%s=%s&%s=%s", objectParameterName,
-							UriUtils.encode(samlObject, StandardCharsets.ISO_8859_1), Saml2ParameterNames.RELAY_STATE,
-							UriUtils.encode(relayState, StandardCharsets.ISO_8859_1), Saml2ParameterNames.SIG_ALG,
-							UriUtils.encode(algorithm, StandardCharsets.ISO_8859_1)).getBytes(StandardCharsets.UTF_8);
-				}
-				else {
-					return String.format("%s=%s&%s=%s", objectParameterName,
-							UriUtils.encode(samlObject, StandardCharsets.ISO_8859_1), Saml2ParameterNames.SIG_ALG,
-							UriUtils.encode(algorithm, StandardCharsets.ISO_8859_1)).getBytes(StandardCharsets.UTF_8);
-				}
+				this.content = UriComponentsBuilder.newInstance().query(response.getParametersQuery())
+						.replaceQueryParam(Saml2ParameterNames.SIGNATURE).build(true).toUriString().substring(1)
+						.getBytes(StandardCharsets.UTF_8);
 			}
 
 			byte[] getContent() {

+ 55 - 5
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutRequest.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 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.
@@ -17,15 +17,19 @@
 package org.springframework.security.saml2.provider.service.authentication.logout;
 
 import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 import org.springframework.security.saml2.core.Saml2ParameterNames;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
 import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;
+import org.springframework.web.util.UriComponentsBuilder;
+import org.springframework.web.util.UriUtils;
 
 /**
  * A class that represents a signed and serialized SAML 2.0 Logout Request
@@ -35,6 +39,17 @@ import org.springframework.security.saml2.provider.service.web.authentication.lo
  */
 public final class Saml2LogoutRequest implements Serializable {
 
+	private static final Function<Map<String, String>, String> DEFAULT_ENCODER = (params) -> {
+		if (params.isEmpty()) {
+			return null;
+		}
+		UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
+		for (Map.Entry<String, String> component : params.entrySet()) {
+			builder.queryParam(component.getKey(), UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));
+		}
+		return builder.build(true).toString().substring(1);
+	};
+
 	private final String location;
 
 	private final Saml2MessageBinding binding;
@@ -45,13 +60,21 @@ public final class Saml2LogoutRequest implements Serializable {
 
 	private final String relyingPartyRegistrationId;
 
+	private Function<Map<String, String>, String> encoder;
+
 	private Saml2LogoutRequest(String location, Saml2MessageBinding binding, Map<String, String> parameters, String id,
 			String relyingPartyRegistrationId) {
+		this(location, binding, parameters, id, relyingPartyRegistrationId, DEFAULT_ENCODER);
+	}
+
+	private Saml2LogoutRequest(String location, Saml2MessageBinding binding, Map<String, String> parameters, String id,
+			String relyingPartyRegistrationId, Function<Map<String, String>, String> encoder) {
 		this.location = location;
 		this.binding = binding;
-		this.parameters = Collections.unmodifiableMap(new HashMap<>(parameters));
+		this.parameters = Collections.unmodifiableMap(new LinkedHashMap<>(parameters));
 		this.id = id;
 		this.relyingPartyRegistrationId = relyingPartyRegistrationId;
+		this.encoder = encoder;
 	}
 
 	/**
@@ -119,6 +142,16 @@ public final class Saml2LogoutRequest implements Serializable {
 		return this.parameters;
 	}
 
+	/**
+	 * Get an encoded query string of all parameters. Resulting query does not contain a
+	 * leading question mark.
+	 * @return an encoded string of all parameters
+	 * @since 5.8
+	 */
+	public String getParametersQuery() {
+		return this.encoder.apply(this.parameters);
+	}
+
 	/**
 	 * The identifier for the {@link RelyingPartyRegistration} associated with this Logout
 	 * Request
@@ -149,7 +182,9 @@ public final class Saml2LogoutRequest implements Serializable {
 
 		private Saml2MessageBinding binding;
 
-		private Map<String, String> parameters = new HashMap<>();
+		private Map<String, String> parameters = new LinkedHashMap<>();
+
+		private Function<Map<String, String>, String> encoder = DEFAULT_ENCODER;
 
 		private String id;
 
@@ -235,13 +270,28 @@ public final class Saml2LogoutRequest implements Serializable {
 			return this;
 		}
 
+		/**
+		 * Use this strategy for converting parameters into an encoded query string. The
+		 * resulting query does not contain a leading question mark.
+		 *
+		 * In the event that you already have an encoded version that you want to use, you
+		 * can call this by doing {@code parameterEncoder((params) -> encodedValue)}.
+		 * @param encoder the strategy to use
+		 * @return the {@link Builder} for further configurations
+		 * @since 5.8
+		 */
+		public Builder parametersQuery(Function<Map<String, String>, String> encoder) {
+			this.encoder = encoder;
+			return this;
+		}
+
 		/**
 		 * Build the {@link Saml2LogoutRequest}
 		 * @return a constructed {@link Saml2LogoutRequest}
 		 */
 		public Saml2LogoutRequest build() {
 			return new Saml2LogoutRequest(this.location, this.binding, this.parameters, this.id,
-					this.registration.getRegistrationId());
+					this.registration.getRegistrationId(), this.encoder);
 		}
 
 	}

+ 52 - 6
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/logout/Saml2LogoutResponse.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 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.
@@ -16,15 +16,19 @@
 
 package org.springframework.security.saml2.provider.service.authentication.logout;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 import org.springframework.security.saml2.core.Saml2ParameterNames;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
 import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver;
+import org.springframework.web.util.UriComponentsBuilder;
+import org.springframework.web.util.UriUtils;
 
 /**
  * A class that represents a signed and serialized SAML 2.0 Logout Response
@@ -34,16 +38,31 @@ import org.springframework.security.saml2.provider.service.web.authentication.lo
  */
 public final class Saml2LogoutResponse {
 
+	private static final Function<Map<String, String>, String> DEFAULT_ENCODER = (params) -> {
+		if (params.isEmpty()) {
+			return null;
+		}
+		UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
+		for (Map.Entry<String, String> component : params.entrySet()) {
+			builder.queryParam(component.getKey(), UriUtils.encode(component.getValue(), StandardCharsets.ISO_8859_1));
+		}
+		return builder.build(true).toString().substring(1);
+	};
+
 	private final String location;
 
 	private final Saml2MessageBinding binding;
 
 	private final Map<String, String> parameters;
 
-	private Saml2LogoutResponse(String location, Saml2MessageBinding binding, Map<String, String> parameters) {
+	private final Function<Map<String, String>, String> encoder;
+
+	private Saml2LogoutResponse(String location, Saml2MessageBinding binding, Map<String, String> parameters,
+			Function<Map<String, String>, String> encoder) {
 		this.location = location;
 		this.binding = binding;
-		this.parameters = Collections.unmodifiableMap(new HashMap<>(parameters));
+		this.parameters = Collections.unmodifiableMap(new LinkedHashMap<>(parameters));
+		this.encoder = encoder;
 	}
 
 	/**
@@ -103,6 +122,16 @@ public final class Saml2LogoutResponse {
 		return this.parameters;
 	}
 
+	/**
+	 * Get an encoded query string of all parameters. Resulting query does not contain a
+	 * leading question mark.
+	 * @return an encoded string of all parameters
+	 * @since 5.8
+	 */
+	public String getParametersQuery() {
+		return this.encoder.apply(this.parameters);
+	}
+
 	/**
 	 * Create a {@link Builder} instance from this {@link RelyingPartyRegistration}
 	 *
@@ -122,7 +151,9 @@ public final class Saml2LogoutResponse {
 
 		private Saml2MessageBinding binding;
 
-		private Map<String, String> parameters = new HashMap<>();
+		private Map<String, String> parameters = new LinkedHashMap<>();
+
+		private Function<Map<String, String>, String> encoder = DEFAULT_ENCODER;
 
 		private Builder(RelyingPartyRegistration registration) {
 			this.location = registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation();
@@ -195,12 +226,27 @@ public final class Saml2LogoutResponse {
 			return this;
 		}
 
+		/**
+		 * Use this strategy for converting parameters into an encoded query string. The
+		 * resulting query does not contain a leading question mark.
+		 *
+		 * In the event that you already have an encoded version that you want to use, you
+		 * can call this by doing {@code parameterEncoder((params) -> encodedValue)}.
+		 * @param encoder the strategy to use
+		 * @return the {@link Saml2LogoutRequest.Builder} for further configurations
+		 * @since 5.8
+		 */
+		public Builder parametersQuery(Function<Map<String, String>, String> encoder) {
+			this.encoder = encoder;
+			return this;
+		}
+
 		/**
 		 * Build the {@link Saml2LogoutResponse}
 		 * @return a constructed {@link Saml2LogoutResponse}
 		 */
 		public Saml2LogoutResponse build() {
-			return new Saml2LogoutResponse(this.location, this.binding, this.parameters);
+			return new Saml2LogoutResponse(this.location, this.binding, this.parameters, this.encoder);
 		}
 
 	}

+ 4 - 18
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 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.
@@ -17,8 +17,6 @@
 package org.springframework.security.saml2.provider.service.web.authentication.logout;
 
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.function.Function;
 
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
@@ -52,7 +50,6 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.filter.OncePerRequestFilter;
 import org.springframework.web.util.HtmlUtils;
 import org.springframework.web.util.UriComponentsBuilder;
-import org.springframework.web.util.UriUtils;
 
 /**
  * A filter for handling logout requests in the form of a &lt;saml2:LogoutRequest&gt; sent
@@ -140,7 +137,7 @@ public final class Saml2LogoutRequestFilter extends OncePerRequestFilter {
 						request.getParameter(Saml2ParameterNames.SIG_ALG)))
 				.parameters((params) -> params.put(Saml2ParameterNames.SIGNATURE,
 						request.getParameter(Saml2ParameterNames.SIGNATURE)))
-				.build();
+				.parametersQuery((params) -> request.getQueryString()).build();
 		Saml2LogoutRequestValidatorParameters parameters = new Saml2LogoutRequestValidatorParameters(logoutRequest,
 				registration, authentication);
 		Saml2LogoutValidatorResult result = this.logoutRequestValidator.validate(parameters);
@@ -191,22 +188,11 @@ public final class Saml2LogoutRequestFilter extends OncePerRequestFilter {
 	private void doRedirect(HttpServletRequest request, HttpServletResponse response,
 			Saml2LogoutResponse logoutResponse) throws IOException {
 		String location = logoutResponse.getResponseLocation();
-		UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location);
-		addParameter(Saml2ParameterNames.SAML_RESPONSE, logoutResponse::getParameter, uriBuilder);
-		addParameter(Saml2ParameterNames.RELAY_STATE, logoutResponse::getParameter, uriBuilder);
-		addParameter(Saml2ParameterNames.SIG_ALG, logoutResponse::getParameter, uriBuilder);
-		addParameter(Saml2ParameterNames.SIGNATURE, logoutResponse::getParameter, uriBuilder);
+		UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location)
+				.query(logoutResponse.getParametersQuery());
 		this.redirectStrategy.sendRedirect(request, response, uriBuilder.build(true).toUriString());
 	}
 
-	private void addParameter(String name, Function<String, String> parameters, UriComponentsBuilder builder) {
-		Assert.hasText(name, "name cannot be empty or null");
-		if (StringUtils.hasText(parameters.apply(name))) {
-			builder.queryParam(UriUtils.encode(name, StandardCharsets.ISO_8859_1),
-					UriUtils.encode(parameters.apply(name), StandardCharsets.ISO_8859_1));
-		}
-	}
-
 	private void doPost(HttpServletResponse response, Saml2LogoutResponse logoutResponse) throws IOException {
 		String location = logoutResponse.getResponseLocation();
 		String saml = logoutResponse.getSamlResponse();

+ 2 - 2
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutResponseFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 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.
@@ -140,7 +140,7 @@ public final class Saml2LogoutResponseFilter extends OncePerRequestFilter {
 						request.getParameter(Saml2ParameterNames.SIG_ALG)))
 				.parameters((params) -> params.put(Saml2ParameterNames.SIGNATURE,
 						request.getParameter(Saml2ParameterNames.SIGNATURE)))
-				.build();
+				.parametersQuery((params) -> request.getQueryString()).build();
 		Saml2LogoutResponseValidatorParameters parameters = new Saml2LogoutResponseValidatorParameters(logoutResponse,
 				logoutRequest, registration);
 		Saml2LogoutValidatorResult result = this.logoutResponseValidator.validate(parameters);

+ 3 - 18
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2RelyingPartyInitiatedLogoutSuccessHandler.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 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.
@@ -17,8 +17,6 @@
 package org.springframework.security.saml2.provider.service.web.authentication.logout;
 
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.function.Function;
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
@@ -27,7 +25,6 @@ import org.apache.commons.logging.LogFactory;
 
 import org.springframework.http.MediaType;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.saml2.core.Saml2ParameterNames;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
 import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
 import org.springframework.security.web.DefaultRedirectStrategy;
@@ -37,7 +34,6 @@ import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 import org.springframework.web.util.HtmlUtils;
 import org.springframework.web.util.UriComponentsBuilder;
-import org.springframework.web.util.UriUtils;
 
 /**
  * A success handler for issuing a SAML 2.0 Logout Request to the the SAML 2.0 Asserting
@@ -104,22 +100,11 @@ public final class Saml2RelyingPartyInitiatedLogoutSuccessHandler implements Log
 	private void doRedirect(HttpServletRequest request, HttpServletResponse response, Saml2LogoutRequest logoutRequest)
 			throws IOException {
 		String location = logoutRequest.getLocation();
-		UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location);
-		addParameter(Saml2ParameterNames.SAML_REQUEST, logoutRequest::getParameter, uriBuilder);
-		addParameter(Saml2ParameterNames.RELAY_STATE, logoutRequest::getParameter, uriBuilder);
-		addParameter(Saml2ParameterNames.SIG_ALG, logoutRequest::getParameter, uriBuilder);
-		addParameter(Saml2ParameterNames.SIGNATURE, logoutRequest::getParameter, uriBuilder);
+		UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(location)
+				.query(logoutRequest.getParametersQuery());
 		this.redirectStrategy.sendRedirect(request, response, uriBuilder.build(true).toUriString());
 	}
 
-	private void addParameter(String name, Function<String, String> parameters, UriComponentsBuilder builder) {
-		Assert.hasText(name, "name cannot be empty or null");
-		if (StringUtils.hasText(parameters.apply(name))) {
-			builder.queryParam(UriUtils.encode(name, StandardCharsets.ISO_8859_1),
-					UriUtils.encode(parameters.apply(name), StandardCharsets.ISO_8859_1));
-		}
-	}
-
 	private void doPost(HttpServletResponse response, Saml2LogoutRequest logoutRequest) throws IOException {
 		String location = logoutRequest.getLocation();
 		String saml = logoutRequest.getSamlRequest();