如何在Spring Security SAML2 SP元数据中添加RequestedAttribute?
如何在Spring Security SAML2 SP元数据中添加RequestedAttribute?
部分身份提供商(IDP)默认不会返回所有请求的属性,我希望在服务提供商(SP)的SAML元数据中声明这些属性,以此鼓励IDP释放这些属性。目前已经能配置nameid-format这类元数据,但找不到添加RequestedAttribute条目的方法,尝试过.saml2Metadata()和.saml2Login()的配置器都没找到对应入口。
使用的POM依赖
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-saml2-service-provider</artifactId> <version>6.5.5</version> <exclusions> <exclusion> <groupId>org.opensaml</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.opensaml</groupId> <artifactId>opensaml-saml-api</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>org.opensaml</groupId> <artifactId>opensaml-saml-impl</artifactId> <version>5.1.6</version> </dependency>
RelyingPartyRegistration构建代码
public RelyingPartyRegistration apply(RelyingPartyRegistration.Builder builder) { Saml2X509Credential signing = Saml2X509Credential.signing(this.key, this.certificate); Saml2X509Credential decryption = Saml2X509Credential.decryption(key, certificate); return builder.entityId(this.entityId) .assertionConsumerServiceLocation(this.sso) .singleLogoutServiceBinding(this.slo.getBinding()) .singleLogoutServiceLocation(this.slo.getLocation()) .singleLogoutServiceResponseLocation(this.slo.getResponseLocation()) .signingX509Credentials((c) -> c.add(signing)) .decryptionX509Credentials((d) -> d.add(decryption)) .nameIdFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent") .build(); }
SecurityFilterChain配置
.saml2Login(saml2 -> { saml2.loginPage("/Shibboleth/"); saml2.authenticationManager(new ProviderManager(authenticationProvider)); saml2.successHandler(loginSuccessHandlerService); saml2.failureHandler(customAuthenticationFailureHandler); }) .saml2Metadata(metadataConfigurer -> { metadataConfigurer.metadataUrl("/saml/metadata"); })
当前生成的元数据
<?xml version="1.0" encoding="UTF-8"?> <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="--redacted--"> <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:KeyDescriptor use="signing"> <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:X509Data> <ds:X509Certificate>--redacted--</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </md:KeyDescriptor> <md:KeyDescriptor use="encryption"> <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:X509Data> <ds:X509Certificate>--redacted--</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </md:KeyDescriptor> <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://--redacted--/logout/saml2/slo" ResponseLocation="https://--redacted--/logout/saml2/slo"/> <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat> <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://--redacted--/login/saml2/sso" index="1"/> </md:SPSSODescriptor> </md:EntityDescriptor>
期望生成的元数据
<?xml version="1.0" encoding="UTF-8"?> <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="--redacted--"> <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:KeyDescriptor use="signing"> <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:X509Data> <ds:X509Certificate>--redacted--</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </md:KeyDescriptor> <md:KeyDescriptor use="encryption"> <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:X509Data> <ds:X509Certificate>--redacted--</ds:X509Certificate> </ds:X509Data> </ds:KeyInfo> </md:KeyDescriptor> <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://--redacted--/logout/saml2/slo" ResponseLocation="https://--redacted--/logout/saml2/slo"/> <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat> <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://--redacted--/login/saml2/sso" index="1"/> <md:AttributeConsumingService isDefault="true" index="1"> <md:ServiceName xml:lang="en">Service Provider Portal</md:ServiceName> <md:RequestedAttribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" FriendlyName="eduPersonAffiliation"> </md:RequestedAttribute> </md:AttributeConsumingService> </md:SPSSODescriptor> </md:EntityDescriptor>
解决方案
Spring Security SAML2 6.x版本未提供直接添加AttributeConsumingService和RequestedAttribute的配置API,需要通过自定义元数据生成器实现:
1. 自定义元数据生成器
继承默认的OpenSamlMetadataGenerator,重写元数据填充逻辑:
import org.opensaml.saml.saml2.metadata.AttributeConsumingService; import org.opensaml.saml.saml2.metadata.RequestedAttribute; import org.opensaml.saml.saml2.metadata.ServiceName; import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataGenerator; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; public class CustomSaml2MetadataGenerator extends OpenSamlMetadataGenerator { public CustomSaml2MetadataGenerator(RelyingPartyRegistration registration) { super(registration); } @Override protected void populateSPSSODescriptor(SPSSODescriptor descriptor) { super.populateSPSSODescriptor(descriptor); // 构建AttributeConsumingService节点 AttributeConsumingService attributeService = buildSAMLObject(AttributeConsumingService.class); attributeService.setIndex(1); attributeService.setIsDefault(true); // 添加服务名称 ServiceName serviceName = buildSAMLObject(ServiceName.class); serviceName.setLang("en"); serviceName.setValue("Service Provider Portal"); attributeService.getNames().add(serviceName); // 添加RequestedAttribute RequestedAttribute requestedAttr = buildSAMLObject(RequestedAttribute.class); requestedAttr.setName("urn:oid:1.3.6.1.4.1.5923.1.1.1.1"); requestedAttr.setFriendlyName("eduPersonAffiliation"); requestedAttr.setNameFormat("urn:oasis:names:tc:SAML:2.0:attrname-format:uri"); attributeService.getRequestedAttributes().add(requestedAttr); descriptor.getAttributeConsumingServices().add(attributeService); } }
2. 注册自定义生成器
在SecurityFilterChain配置中替换默认的元数据生成器:
.saml2Metadata(metadataConfigurer -> { metadataConfigurer.metadataUrl("/saml/metadata") .metadataGenerator((registration) -> new CustomSaml2MetadataGenerator(registration)); })
3. 补充说明
- 代码依赖OpenSAML API构建元数据节点,确保依赖中包含
opensaml-saml-api和opensaml-saml-impl - 若需添加多个
RequestedAttribute,重复创建对象并加入列表即可 - 旧版
MetadataGenerator已被移除,6.x版本默认使用OpenSamlMetadataGenerator实现
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

