mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 06:38:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			241 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| [[servlet-saml2login-metadata]]
 | |
| = Saml 2.0 Metadata
 | |
| 
 | |
| Spring Security can <<parsing-asserting-party-metadata,parse asserting party metadata>> to produce an `AssertingPartyMetadata` instance as well as <<publishing-relying-party-metadata,publish relying party metadata>> from a `RelyingPartyRegistration` instance.
 | |
| 
 | |
| [[parsing-asserting-party-metadata]]
 | |
| == Parsing `<saml2:IDPSSODescriptor>` metadata
 | |
| 
 | |
| You can parse an asserting party's metadata xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[using `RelyingPartyRegistrations`].
 | |
| 
 | |
| When using the OpenSAML vendor support, the resulting `AssertingPartyMetadata` will be of type `OpenSamlAssertingPartyDetails`.
 | |
| This means you'll be able to do get the underlying OpenSAML XMLObject by doing the following:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| OpenSamlAssertingPartyDetails details = (OpenSamlAssertingPartyDetails)
 | |
|         registration.getAssertingPartyMetadata();
 | |
| EntityDescriptor openSamlEntityDescriptor = details.getEntityDescriptor();
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| val details: OpenSamlAssertingPartyDetails =
 | |
|         registration.getAssertingPartyMetadata() as OpenSamlAssertingPartyDetails
 | |
| val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor()
 | |
| ----
 | |
| ======
 | |
| 
 | |
| [[using-assertingpartymetadatarepository]]
 | |
| === Using `AssertingPartyMetadataRepository`
 | |
| 
 | |
| You can also be more targeted than `RelyingPartyRegistrations` by using `AssertingPartyMetadataRepository`, an interface that allows for only retrieving the asserting party metadata.
 | |
| 
 | |
| This allows three valuable features:
 | |
| 
 | |
| * Implementations can refresh asserting party metadata in an expiry-aware fashion
 | |
| * Implementations of `RelyingPartyRegistrationRepository` can more easily articulate a relationship between a relying party and its one or many corresponding asserting parties
 | |
| * Implementations can verify metadata signatures
 | |
| 
 | |
| For example, `OpenSaml4AssertingPartyMetadataRepository` uses OpenSAML's `MetadataResolver`, and API whose implementations regularly refresh the underlying metadata in an expiry-aware fashion.
 | |
| 
 | |
| This means that you can now create a refreshable `RelyingPartyRegistrationRepository` in just a few lines of code:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @Component
 | |
| public class RefreshableRelyingPartyRegistrationRepository
 | |
|         implements IterableRelyingPartyRegistrationRepository {
 | |
| 
 | |
| 	private final AssertingPartyMetadataRepository metadata =
 | |
|             OpenSamlAssertingPartyMetadataRepository
 | |
|                 .fromTrustedMetadataLocation("https://idp.example.org/metadata").build();
 | |
| 
 | |
| 	@Override
 | |
|     public RelyingPartyRegistration findByRegistrationId(String registrationId) {
 | |
| 		AssertingPartyMetadata metadata = this.metadata.findByEntityId(registrationId);
 | |
|         if (metadata == null) {
 | |
|             return null;
 | |
|         }
 | |
| 		return applyRelyingParty(metadata);
 | |
|     }
 | |
| 
 | |
| 	@Override
 | |
|     public Iterator<RelyingPartyRegistration> iterator() {
 | |
| 		return StreamSupport.stream(this.metadata.spliterator(), false)
 | |
|             .map(this::applyRelyingParty).iterator();
 | |
|     }
 | |
| 
 | |
| 	private RelyingPartyRegistration applyRelyingParty(AssertingPartyMetadata metadata) {
 | |
| 		return RelyingPartyRegistration.withAssertingPartyMetadata(metadata)
 | |
|             // apply any relying party configuration
 | |
|             .build();
 | |
| 	}
 | |
| 
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @Component
 | |
| class RefreshableRelyingPartyRegistrationRepository : IterableRelyingPartyRegistrationRepository {
 | |
| 
 | |
|     private val metadata: AssertingPartyMetadataRepository =
 | |
|         OpenSamlAssertingPartyMetadataRepository.fromTrustedMetadataLocation(
 | |
|             "https://idp.example.org/metadata").build()
 | |
| 
 | |
|     fun findByRegistrationId(registrationId:String?): RelyingPartyRegistration {
 | |
|         val metadata = this.metadata.findByEntityId(registrationId)
 | |
|         if (metadata == null) {
 | |
|             return null
 | |
|         }
 | |
|         return applyRelyingParty(metadata)
 | |
|     }
 | |
| 
 | |
|     fun iterator(): Iterator<RelyingPartyRegistration> {
 | |
|         return StreamSupport.stream(this.metadata.spliterator(), false)
 | |
|             .map(this::applyRelyingParty).iterator()
 | |
|     }
 | |
| 
 | |
|     private fun applyRelyingParty(metadata: AssertingPartyMetadata): RelyingPartyRegistration {
 | |
|         val details: AssertingPartyMetadata = metadata as AssertingPartyMetadata
 | |
|         return RelyingPartyRegistration.withAssertingPartyMetadata(details)
 | |
|             // apply any relying party configuration
 | |
|             .build()
 | |
|     }
 | |
|  }
 | |
| ----
 | |
| ======
 | |
| 
 | |
| [TIP]
 | |
| `OpenSaml4AssertingPartyMetadataRepository` also ships with a constructor so you can provide a custom `MetadataResolver`. Since the underlying `MetadataResolver` is doing the expiring and refreshing, if you use the constructor directly, you will only get these features by providing an implementation that does so.
 | |
| 
 | |
| === Verifying Metadata Signatures
 | |
| 
 | |
| You can also verify metadata signatures using `OpenSaml4AssertingPartyMetadataRepository` by providing the appropriate set of ``Saml2X509Credential``s as follows:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| OpenSamlAssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
 | |
|     .verificationCredentials((c) -> c.add(myVerificationCredential))
 | |
|     .build();
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| OpenSamlAssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
 | |
|     .verificationCredentials({ c : Collection<Saml2X509Credential> ->
 | |
|         c.add(myVerificationCredential) })
 | |
|     .build()
 | |
| ----
 | |
| ======
 | |
| 
 | |
| [NOTE]
 | |
| If no credentials are provided, the component will not perform signature validation.
 | |
| 
 | |
| [[publishing-relying-party-metadata]]
 | |
| == Producing `<saml2:SPSSODescriptor>` Metadata
 | |
| 
 | |
| You can publish a metadata endpoint using the `saml2Metadata` DSL method, as you'll see below:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| http
 | |
|     // ...
 | |
|     .saml2Login(withDefaults())
 | |
|     .saml2Metadata(withDefaults());
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| http {
 | |
|     //...
 | |
|     saml2Login { }
 | |
|     saml2Metadata { }
 | |
| }
 | |
| ----
 | |
| ======
 | |
| 
 | |
| You can use this metadata endpoint to register your relying party with your asserting party.
 | |
| This is often as simple as finding the correct form field to supply the metadata endpoint.
 | |
| 
 | |
| By default, the metadata endpoint is `+/saml2/metadata+`, though it also responds to `+/saml2/metadata/{registrationId}+` and `+/saml2/service-provider-metadata/{registrationId}+`.
 | |
| 
 | |
| You can change this by calling the `metadataUrl` method in the DSL:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| .saml2Metadata((saml2) -> saml2.metadataUrl("/saml/metadata"))
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| saml2Metadata {
 | |
| 	metadataUrl = "/saml/metadata"
 | |
| }
 | |
| ----
 | |
| ======
 | |
| 
 | |
| == Changing the Way a `RelyingPartyRegistration` Is Looked Up
 | |
| 
 | |
| If you have a different strategy for identifying which `RelyingPartyRegistration` to use, you can configure your own `Saml2MetadataResponseResolver` like the one below:
 | |
| 
 | |
| [tabs]
 | |
| ======
 | |
| Java::
 | |
| +
 | |
| [source,java,role="primary"]
 | |
| ----
 | |
| @Bean
 | |
| Saml2MetadataResponseResolver metadataResponseResolver(RelyingPartyRegistrationRepository registrations) {
 | |
| 	RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(
 | |
| 			(id) -> registrations.findByRegistrationId("relying-party"));
 | |
| 	metadata.setMetadataFilename("metadata.xml");
 | |
| 	return metadata;
 | |
| }
 | |
| ----
 | |
| 
 | |
| Kotlin::
 | |
| +
 | |
| [source,kotlin,role="secondary"]
 | |
| ----
 | |
| @Bean
 | |
| fun metadataResponseResolver(val registrations: RelyingPartyRegistrationRepository): Saml2MetadataResponseResolver {
 | |
|     val metadata = new RequestMatcherMetadataResponseResolver(
 | |
| 			id: String -> registrations.findByRegistrationId("relying-party"))
 | |
| 	metadata.setMetadataFilename("metadata.xml")
 | |
| 	return metadata
 | |
| }
 | |
| ----
 | |
| ======
 |