浏览代码

SAML 2.0 Single Logout Uses Saml2AuthenticationInfo

This allows SLO to be triggered without the authentication
principal needing to implement a given interface.

Issue gh-10820
Christian Schuster 3 年之前
父节点
当前提交
36c7b91fb9

+ 2 - 5
config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java

@@ -33,7 +33,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
 import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
 import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
 import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
 import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
@@ -531,10 +531,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
 		@Override
 		public boolean matches(HttpServletRequest request) {
 			Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
-			if (authentication == null) {
-				return false;
-			}
-			return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
+			return Saml2AuthenticationInfo.fromAuthentication(authentication) != null;
 		}
 
 	}

+ 2 - 5
config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java

@@ -31,7 +31,7 @@ import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
 import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
 import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
 import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
@@ -236,10 +236,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
 		@Override
 		public boolean matches(HttpServletRequest request) {
 			Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
-			if (authentication == null) {
-				return false;
-			}
-			return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
+			return Saml2AuthenticationInfo.fromAuthentication(authentication) != null;
 		}
 
 		public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {

+ 3 - 1
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java

@@ -31,7 +31,7 @@ import org.springframework.util.CollectionUtils;
  * @author Clement Stoquart
  * @since 5.2.2
  */
-public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
+public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal, Saml2AuthenticationInfo {
 
 	/**
 	 * Get the first value of Saml2 token attribute by name
@@ -72,10 +72,12 @@ public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
 	 * @return the {@link RelyingPartyRegistration} identifier
 	 * @since 5.6
 	 */
+	@Override
 	default String getRelyingPartyRegistrationId() {
 		return null;
 	}
 
+	@Override
 	default List<String> getSessionIndexes() {
 		return Collections.emptyList();
 	}

+ 78 - 0
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationInfo.java

@@ -0,0 +1,78 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.saml2.provider.service.authentication;
+
+import java.util.List;
+
+import org.opensaml.saml.saml2.core.SessionIndex;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
+
+/**
+ * Additional SAML 2.0 authentication information
+ *
+ * <p>
+ * SAML 2.0 Single Logout requires that the {@link Authentication#getPrincipal()
+ * authenticated principal} or the {@link Authentication} itself implements this
+ * interface.
+ *
+ * @author Christian Schuster
+ */
+public interface Saml2AuthenticationInfo {
+
+	/**
+	 * Get the {@link RelyingPartyRegistration} identifier
+	 * @return the {@link RelyingPartyRegistration} identifier
+	 */
+	String getRelyingPartyRegistrationId();
+
+	/**
+	 * Get the {@link SessionIndex} values of the authenticated principal
+	 * @return the {@link SessionIndex} values of the authenticated principal
+	 */
+	List<String> getSessionIndexes();
+
+	/**
+	 * Try to obtain a {@link Saml2AuthenticationInfo} instance from an
+	 * {@link Authentication}
+	 *
+	 * <p>
+	 * The result is either the {@link Authentication#getPrincipal() authenticated
+	 * principal}, the {@link Authentication} itself, or {@code null}.
+	 *
+	 * <p>
+	 * Returning {@code null} indicates that the given {@link Authentication} does not
+	 * represent a SAML 2.0 authentication.
+	 * @param authentication the {@link Authentication}
+	 * @return the {@link Saml2AuthenticationInfo} or {@code null} if unavailable
+	 */
+	static Saml2AuthenticationInfo fromAuthentication(Authentication authentication) {
+		if (authentication == null) {
+			return null;
+		}
+		Object principal = authentication.getPrincipal();
+		if (principal instanceof Saml2AuthenticationInfo) {
+			return (Saml2AuthenticationInfo) principal;
+		}
+		if (authentication instanceof Saml2AuthenticationInfo) {
+			return (Saml2AuthenticationInfo) authentication;
+		}
+		return null;
+	}
+
+}

+ 7 - 10
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutRequestResolver.java

@@ -42,7 +42,7 @@ import org.springframework.core.convert.converter.Converter;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.saml2.core.OpenSamlInitializationService;
 import org.springframework.security.saml2.core.Saml2ParameterNames;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
@@ -149,9 +149,9 @@ final class BaseOpenSamlLogoutRequestResolver implements Saml2LogoutRequestResol
 		NameID nameId = this.nameIdBuilder.buildObject();
 		nameId.setValue(authentication.getName());
 		logoutRequest.setNameID(nameId);
-		if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
-			Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();
-			for (String index : principal.getSessionIndexes()) {
+		Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
+		if (info != null) {
+			for (String index : info.getSessionIndexes()) {
 				SessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();
 				sessionIndex.setValue(index);
 				logoutRequest.getSessionIndexes().add(sessionIndex);
@@ -191,12 +191,9 @@ final class BaseOpenSamlLogoutRequestResolver implements Saml2LogoutRequestResol
 		if (this.logger.isTraceEnabled()) {
 			this.logger.trace("Attempting to resolve registrationId from " + authentication);
 		}
-		if (authentication == null) {
-			return null;
-		}
-		Object principal = authentication.getPrincipal();
-		if (principal instanceof Saml2AuthenticatedPrincipal) {
-			return ((Saml2AuthenticatedPrincipal) principal).getRelyingPartyRegistrationId();
+		Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
+		if (info != null) {
+			return info.getRelyingPartyRegistrationId();
 		}
 		return null;
 	}

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

@@ -24,8 +24,8 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.saml2.core.OpenSamlInitializationService;
 import org.springframework.security.saml2.core.Saml2Error;
 import org.springframework.security.saml2.core.Saml2ParameterNames;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
 import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
@@ -130,11 +130,9 @@ final class BaseOpenSamlLogoutRequestValidatorParametersResolver
 		if (registrationId != null) {
 			return registrationId;
 		}
-		if (authentication == null) {
-			return null;
-		}
-		if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
-			return principal.getRelyingPartyRegistrationId();
+		Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
+		if (info != null) {
+			return info.getRelyingPartyRegistrationId();
 		}
 		return null;
 	}

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

@@ -46,8 +46,8 @@ import org.springframework.security.saml2.core.OpenSamlInitializationService;
 import org.springframework.security.saml2.core.Saml2Error;
 import org.springframework.security.saml2.core.Saml2ErrorCodes;
 import org.springframework.security.saml2.core.Saml2ParameterNames;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
 import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
@@ -217,12 +217,9 @@ final class BaseOpenSamlLogoutResponseResolver implements Saml2LogoutResponseRes
 		if (this.logger.isTraceEnabled()) {
 			this.logger.trace("Attempting to resolve registrationId from " + authentication);
 		}
-		if (authentication == null) {
-			return null;
-		}
-		Object principal = authentication.getPrincipal();
-		if (principal instanceof Saml2AuthenticatedPrincipal) {
-			return ((Saml2AuthenticatedPrincipal) principal).getRelyingPartyRegistrationId();
+		Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
+		if (info != null) {
+			return info.getRelyingPartyRegistrationId();
 		}
 		return null;
 	}

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

@@ -33,8 +33,8 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.security.saml2.core.Saml2Error;
 import org.springframework.security.saml2.core.Saml2ErrorCodes;
 import org.springframework.security.saml2.core.Saml2ParameterNames;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
 import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
@@ -329,11 +329,9 @@ public final class Saml2LogoutRequestFilter extends OncePerRequestFilter {
 			if (registrationId != null) {
 				return registrationId;
 			}
-			if (authentication == null) {
-				return null;
-			}
-			if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
-				return principal.getRelyingPartyRegistrationId();
+			Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
+			if (info != null) {
+				return info.getRelyingPartyRegistrationId();
 			}
 			return null;
 		}

+ 4 - 6
saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestValidatorParametersResolver.java

@@ -28,8 +28,8 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.saml2.core.OpenSamlInitializationService;
 import org.springframework.security.saml2.core.Saml2Error;
 import org.springframework.security.saml2.core.Saml2ParameterNames;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
 import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
 import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
@@ -144,11 +144,9 @@ public final class OpenSamlLogoutRequestValidatorParametersResolver
 		if (registrationId != null) {
 			return registrationId;
 		}
-		if (authentication == null) {
-			return null;
-		}
-		if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
-			return principal.getRelyingPartyRegistrationId();
+		Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
+		if (info != null) {
+			return info.getRelyingPartyRegistrationId();
 		}
 		return null;
 	}