parent
785123eb2a
commit
46452c0cae
|
@ -73,6 +73,7 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
|
||||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
@ -2425,6 +2426,102 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
return getOrApply(new Saml2LogoutConfigurer<>(getContext()));
|
return getOrApply(new Saml2LogoutConfigurer<>(getContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a SAML 2.0 metadata endpoint that presents relying party configurations
|
||||||
|
* in an {@code <md:EntityDescriptor>} payload.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By default, the endpoints are {@code /saml2/metadata} and
|
||||||
|
* {@code /saml2/metadata/{registrationId}} though note that also
|
||||||
|
* {@code /saml2/service-provider-metadata/{registrationId}} is recognized for
|
||||||
|
* backward compatibility purposes.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <h2>Example Configuration</h2>
|
||||||
|
*
|
||||||
|
* The following example shows the minimal configuration required, using a
|
||||||
|
* hypothetical asserting party.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* @Configuration
|
||||||
|
* public class Saml2LogoutSecurityConfig {
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain web(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
|
||||||
|
* .saml2Metadata(Customizer.withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
|
||||||
|
* RelyingPartyRegistration registration = RelyingPartyRegistrations
|
||||||
|
* .withMetadataLocation("https://ap.example.org/metadata")
|
||||||
|
* .registrationId("simple")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryRelyingPartyRegistrationRepository(registration);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* @param saml2MetadataConfigurer the {@link Customizer} to provide more options for
|
||||||
|
* the {@link Saml2MetadataConfigurer}
|
||||||
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @throws Exception
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public HttpSecurity saml2Metadata(Customizer<Saml2MetadataConfigurer<HttpSecurity>> saml2MetadataConfigurer)
|
||||||
|
throws Exception {
|
||||||
|
saml2MetadataConfigurer.customize(getOrApply(new Saml2MetadataConfigurer<>(getContext())));
|
||||||
|
return HttpSecurity.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a SAML 2.0 metadata endpoint that presents relying party configurations
|
||||||
|
* in an {@code <md:EntityDescriptor>} payload.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By default, the endpoints are {@code /saml2/metadata} and
|
||||||
|
* {@code /saml2/metadata/{registrationId}} though note that also
|
||||||
|
* {@code /saml2/service-provider-metadata/{registrationId}} is recognized for
|
||||||
|
* backward compatibility purposes.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <h2>Example Configuration</h2>
|
||||||
|
*
|
||||||
|
* The following example shows the minimal configuration required, using a
|
||||||
|
* hypothetical asserting party.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* @Configuration
|
||||||
|
* public class Saml2LogoutSecurityConfig {
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain web(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
|
||||||
|
* .saml2Metadata(Customizer.withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
|
||||||
|
* RelyingPartyRegistration registration = RelyingPartyRegistrations
|
||||||
|
* .withMetadataLocation("https://ap.example.org/metadata")
|
||||||
|
* .registrationId("simple")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryRelyingPartyRegistrationRepository(registration);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* @return the {@link Saml2MetadataConfigurer} for further customizations
|
||||||
|
* @throws Exception
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public Saml2MetadataConfigurer<HttpSecurity> saml2Metadata() throws Exception {
|
||||||
|
return getOrApply(new Saml2MetadataConfigurer<>(getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0
|
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0
|
||||||
* Provider. <br>
|
* Provider. <br>
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 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.config.annotation.web.configurers.saml2;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver;
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.RequestMatcherMetadataResponseResolver;
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||||
|
import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;
|
||||||
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AbstractHttpConfigurer} for SAML 2.0 Metadata.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* SAML 2.0 Metadata provides an application with the capability to publish configuration
|
||||||
|
* information as a {@code <md:EntityDescriptor>} or {@code <md:EntitiesDescriptor>}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Defaults are provided for all configuration options with the only required
|
||||||
|
* configuration being a {@link Saml2LoginConfigurer#relyingPartyRegistrationRepository}.
|
||||||
|
* Alternatively, a {@link RelyingPartyRegistrationRepository} {@code @Bean} may be
|
||||||
|
* registered instead.
|
||||||
|
*
|
||||||
|
* <h2>Security Filters</h2>
|
||||||
|
*
|
||||||
|
* The following {@code Filter} is populated:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Saml2MetadataFilter}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h2>Shared Objects Created</h2>
|
||||||
|
*
|
||||||
|
* none
|
||||||
|
*
|
||||||
|
* <h2>Shared Objects Used</h2>
|
||||||
|
*
|
||||||
|
* The following shared objects are used:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link RelyingPartyRegistrationRepository} (required)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
* @see HttpSecurity#saml2Metadata()
|
||||||
|
* @see Saml2MetadataFilter
|
||||||
|
* @see RelyingPartyRegistrationRepository
|
||||||
|
*/
|
||||||
|
public class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
|
extends AbstractHttpConfigurer<Saml2LogoutConfigurer<H>, H> {
|
||||||
|
|
||||||
|
private final ApplicationContext context;
|
||||||
|
|
||||||
|
private Function<RelyingPartyRegistrationRepository, Saml2MetadataResponseResolver> metadataResponseResolver;
|
||||||
|
|
||||||
|
public Saml2MetadataConfigurer(ApplicationContext context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this endpoint to request relying party metadata.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If you specify a {@code registrationId} placeholder in the URL, then the filter
|
||||||
|
* will lookup a {@link RelyingPartyRegistration} using that.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If there is no {@code registrationId} and your
|
||||||
|
* {@link RelyingPartyRegistrationRepository} is {code Iterable}, the metadata
|
||||||
|
* endpoint will try and show all relying parties' metadata in a single
|
||||||
|
* {@code <md:EntitiesDecriptor} element.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If you need a more sophisticated lookup strategy than these, use
|
||||||
|
* {@link #metadataResponseResolver} instead.
|
||||||
|
* @param metadataUrl the url to use
|
||||||
|
* @return the {@link Saml2MetadataConfigurer} for more customizations
|
||||||
|
*/
|
||||||
|
public Saml2MetadataConfigurer<H> metadataUrl(String metadataUrl) {
|
||||||
|
Assert.hasText(metadataUrl, "metadataUrl cannot be empty");
|
||||||
|
this.metadataResponseResolver = (registrations) -> {
|
||||||
|
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(registrations,
|
||||||
|
new OpenSamlMetadataResolver());
|
||||||
|
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
|
||||||
|
return metadata;
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this {@link Saml2MetadataResponseResolver} to parse the request and respond
|
||||||
|
* with SAML 2.0 metadata.
|
||||||
|
* @param metadataResponseResolver to use
|
||||||
|
* @return the {@link Saml2MetadataConfigurer} for more customizations
|
||||||
|
*/
|
||||||
|
public Saml2MetadataConfigurer<H> metadataResponseResolver(Saml2MetadataResponseResolver metadataResponseResolver) {
|
||||||
|
Assert.notNull(metadataResponseResolver, "metadataResponseResolver cannot be null");
|
||||||
|
this.metadataResponseResolver = (registrations) -> metadataResponseResolver;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public H and() {
|
||||||
|
return getBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(H http) throws Exception {
|
||||||
|
Saml2MetadataResponseResolver metadataResponseResolver = createMetadataResponseResolver(http);
|
||||||
|
http.addFilterBefore(new Saml2MetadataFilter(metadataResponseResolver), BasicAuthenticationFilter.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Saml2MetadataResponseResolver createMetadataResponseResolver(H http) {
|
||||||
|
if (this.metadataResponseResolver != null) {
|
||||||
|
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
|
||||||
|
return this.metadataResponseResolver.apply(registrations);
|
||||||
|
}
|
||||||
|
Saml2MetadataResponseResolver metadataResponseResolver = getBeanOrNull(Saml2MetadataResponseResolver.class);
|
||||||
|
if (metadataResponseResolver != null) {
|
||||||
|
return metadataResponseResolver;
|
||||||
|
}
|
||||||
|
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
|
||||||
|
return new RequestMatcherMetadataResponseResolver(registrations, new OpenSamlMetadataResolver());
|
||||||
|
}
|
||||||
|
|
||||||
|
private RelyingPartyRegistrationRepository getRelyingPartyRegistrationRepository(H http) {
|
||||||
|
Saml2LoginConfigurer<H> login = http.getConfigurer(Saml2LoginConfigurer.class);
|
||||||
|
if (login != null) {
|
||||||
|
return login.relyingPartyRegistrationRepository(http);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getBeanOrNull(RelyingPartyRegistrationRepository.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <C> C getBeanOrNull(Class<C> clazz) {
|
||||||
|
if (this.context == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (this.context.getBeanNamesForType(clazz).length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.context.getBean(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -677,6 +677,43 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||||
this.http.saml2Login(saml2LoginCustomizer)
|
this.http.saml2Login(saml2LoginCustomizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a SAML 2.0 relying party metadata endpoint.
|
||||||
|
*
|
||||||
|
* A [RelyingPartyRegistrationRepository] is required and must be registered with
|
||||||
|
* the [ApplicationContext] or configured via
|
||||||
|
* [Saml2Dsl.relyingPartyRegistrationRepository]
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* The following example shows the minimal configuration required, using a
|
||||||
|
* hypothetical asserting party.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* class SecurityConfig {
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
* http {
|
||||||
|
* saml2Login { }
|
||||||
|
* saml2Metadata { }
|
||||||
|
* }
|
||||||
|
* return http.build()
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* @param saml2MetadataConfiguration custom configuration to configure the
|
||||||
|
* SAML2 relying party metadata endpoint
|
||||||
|
* @see [Saml2MetadataDsl]
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
fun saml2Metadata(saml2MetadataConfiguration: Saml2MetadataDsl.() -> Unit) {
|
||||||
|
val saml2MetadataCustomizer = Saml2MetadataDsl().apply(saml2MetadataConfiguration).get()
|
||||||
|
this.http.saml2Metadata(saml2MetadataCustomizer)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows configuring how an anonymous user is represented.
|
* Allows configuring how an anonymous user is represented.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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.config.annotation.web
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AuthenticationManagerResolver
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.oauth2.resourceserver.JwtDsl
|
||||||
|
import org.springframework.security.config.annotation.web.oauth2.resourceserver.OpaqueTokenDsl
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint
|
||||||
|
import org.springframework.security.web.access.AccessDeniedHandler
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Kotlin DSL to configure [HttpSecurity] SAML 2.0 relying party metadata support using
|
||||||
|
* idiomatic Kotlin code.
|
||||||
|
*
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 6.1
|
||||||
|
* @property metadataUrl the name of the relying party metadata endpoint; defaults to `/saml2/metadata` and `/saml2/metadata/{registrationId}`
|
||||||
|
* @property metadataResponseResolver the [Saml2MetadataResponseResolver] to use for resolving the
|
||||||
|
* metadata request into metadata
|
||||||
|
*/
|
||||||
|
@SecurityMarker
|
||||||
|
class Saml2MetadataDsl {
|
||||||
|
var metadataUrl: String? = null
|
||||||
|
var metadataResponseResolver: Saml2MetadataResponseResolver? = null
|
||||||
|
|
||||||
|
internal fun get(): (Saml2MetadataConfigurer<HttpSecurity>) -> Unit {
|
||||||
|
return { saml2Metadata ->
|
||||||
|
metadataResponseResolver?.also { saml2Metadata.metadataResponseResolver(metadataResponseResolver) }
|
||||||
|
metadataUrl?.also { saml2Metadata.metadataUrl(metadataUrl) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 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.config.annotation.web.configurers.saml2;
|
||||||
|
|
||||||
|
import com.google.common.net.HttpHeaders;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver;
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.RequestMatcherMetadataResponseResolver;
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse;
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Saml2MetadataConfigurer}
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension.class)
|
||||||
|
public class Saml2MetadataConfigurerTests {
|
||||||
|
|
||||||
|
static RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();
|
||||||
|
|
||||||
|
public final SpringTestContext spring = new SpringTestContext(this);
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
MockMvc mvc;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saml2MetadataRegistrationIdWhenDefaultsThenReturnsMetadata() throws Exception {
|
||||||
|
this.spring.register(DefaultConfig.class).autowire();
|
||||||
|
String filename = "saml-" + registration.getRegistrationId() + "-metadata.xml";
|
||||||
|
this.mvc.perform(get("/saml2/metadata/" + registration.getRegistrationId())).andExpect(status().isOk())
|
||||||
|
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(filename)))
|
||||||
|
.andExpect(content().string(containsString("md:EntityDescriptor")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saml2MetadataRegistrationIdWhenWrongIdThenUnauthorized() throws Exception {
|
||||||
|
this.spring.register(DefaultConfig.class).autowire();
|
||||||
|
this.mvc.perform(get("/saml2/metadata/" + registration.getRegistrationId() + "wrong"))
|
||||||
|
.andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saml2MetadataWhenDefaultsThenReturnsMetadata() throws Exception {
|
||||||
|
this.spring.register(DefaultConfig.class).autowire();
|
||||||
|
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isOk())
|
||||||
|
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString("-metadata.xml")))
|
||||||
|
.andExpect(content().string(containsString("md:EntityDescriptor")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saml2MetadataWhenMetadataResponseResolverThenUses() throws Exception {
|
||||||
|
this.spring.register(DefaultConfig.class, MetadataResponseResolverConfig.class).autowire();
|
||||||
|
Saml2MetadataResponseResolver metadataResponseResolver = this.spring.getContext()
|
||||||
|
.getBean(Saml2MetadataResponseResolver.class);
|
||||||
|
given(metadataResponseResolver.resolve(any(HttpServletRequest.class)))
|
||||||
|
.willReturn(new Saml2MetadataResponse("metadata", "filename"));
|
||||||
|
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isOk())
|
||||||
|
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString("filename")))
|
||||||
|
.andExpect(content().string(containsString("metadata")));
|
||||||
|
verify(metadataResponseResolver).resolve(any(HttpServletRequest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saml2MetadataWhenMetadataResponseResolverDslThenUses() throws Exception {
|
||||||
|
this.spring.register(MetadataResponseResolverDslConfig.class).autowire();
|
||||||
|
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isOk())
|
||||||
|
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString("filename")))
|
||||||
|
.andExpect(content().string(containsString("metadata")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void saml2MetadataWhenMetadataUrlThenUses() throws Exception {
|
||||||
|
this.spring.register(MetadataUrlConfig.class).autowire();
|
||||||
|
String filename = "saml-" + registration.getRegistrationId() + "-metadata.xml";
|
||||||
|
this.mvc.perform(get("/saml/metadata")).andExpect(status().isOk())
|
||||||
|
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(filename)))
|
||||||
|
.andExpect(content().string(containsString("md:EntityDescriptor")));
|
||||||
|
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@Import(RelyingPartyRegistrationConfig.class)
|
||||||
|
static class DefaultConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filters(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
|
||||||
|
.saml2Metadata(Customizer.withDefaults());
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@Import(RelyingPartyRegistrationConfig.class)
|
||||||
|
static class MetadataUrlConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filters(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
|
||||||
|
.saml2Metadata((saml2) -> saml2.metadataUrl("/saml/metadata"));
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
// should ignore
|
||||||
|
@Bean
|
||||||
|
Saml2MetadataResponseResolver metadataResponseResolver(RelyingPartyRegistrationRepository registrations) {
|
||||||
|
return new RequestMatcherMetadataResponseResolver(registrations, new OpenSamlMetadataResolver());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@Import(RelyingPartyRegistrationConfig.class)
|
||||||
|
static class MetadataResponseResolverDslConfig {
|
||||||
|
|
||||||
|
Saml2MetadataResponseResolver metadataResponseResolver = mock(Saml2MetadataResponseResolver.class);
|
||||||
|
|
||||||
|
{
|
||||||
|
given(this.metadataResponseResolver.resolve(any(HttpServletRequest.class)))
|
||||||
|
.willReturn(new Saml2MetadataResponse("metadata", "filename"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filters(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
|
||||||
|
.saml2Metadata((saml2) -> saml2.metadataResponseResolver(this.metadataResponseResolver));
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class MetadataResponseResolverConfig {
|
||||||
|
|
||||||
|
Saml2MetadataResponseResolver metadataResponseResolver = mock(Saml2MetadataResponseResolver.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Saml2MetadataResponseResolver metadataResponseResolver() {
|
||||||
|
return this.metadataResponseResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class RelyingPartyRegistrationConfig {
|
||||||
|
|
||||||
|
RelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(registration);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RelyingPartyRegistrationRepository registrations() {
|
||||||
|
return this.registrations;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* 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.config.annotation.web
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkObject
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.assertj.core.api.Assertions
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.springframework.beans.factory.BeanCreationException
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.core.io.ClassPathResource
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager
|
||||||
|
import org.springframework.security.authentication.ProviderManager
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationProvider
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||||
|
import org.springframework.security.config.test.SpringTestContext
|
||||||
|
import org.springframework.security.config.test.SpringTestContextExtension
|
||||||
|
import org.springframework.security.saml2.core.Saml2X509Credential
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse
|
||||||
|
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository
|
||||||
|
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations
|
||||||
|
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter
|
||||||
|
import org.springframework.security.web.SecurityFilterChain
|
||||||
|
import org.springframework.test.web.servlet.MockMvc
|
||||||
|
import org.springframework.test.web.servlet.get
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
|
||||||
|
import java.security.cert.Certificate
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.util.Base64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for [Saml2Dsl]
|
||||||
|
*
|
||||||
|
* @author Eleftheria Stein
|
||||||
|
*/
|
||||||
|
@ExtendWith(SpringTestContextExtension::class)
|
||||||
|
class Saml2MetadataDslTests {
|
||||||
|
@JvmField
|
||||||
|
val spring = SpringTestContext(this)
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `saml2Metadat when no relying party registration repository then exception`() {
|
||||||
|
Assertions.assertThatThrownBy { this.spring.register(Saml2MetadataNoRelyingPartyRegistrationRepoConfig::class.java).autowire() }
|
||||||
|
.isInstanceOf(BeanCreationException::class.java)
|
||||||
|
.hasMessageContaining("relyingPartyRegistrationRepository cannot be null")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
open class Saml2MetadataNoRelyingPartyRegistrationRepoConfig {
|
||||||
|
@Bean
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
saml2Metadata { }
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `metadata endpoint when saml2Metadata configured then metadata returned`() {
|
||||||
|
this.spring.register(Saml2MetadataConfig::class.java).autowire()
|
||||||
|
|
||||||
|
this.mockMvc.get("/saml2/metadata")
|
||||||
|
.andExpect {
|
||||||
|
status { isOk() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
open class Saml2MetadataConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
saml2Metadata { }
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun registrations(): RelyingPartyRegistrationRepository {
|
||||||
|
return InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.full().build())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T : Certificate> loadCert(location: String): T {
|
||||||
|
ClassPathResource(location).inputStream.use { inputStream ->
|
||||||
|
val certFactory = CertificateFactory.getInstance("X.509")
|
||||||
|
return certFactory.generateCertificate(inputStream) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `metadata endpoint when url customized then used`() {
|
||||||
|
this.spring.register(Saml2LoginCustomEndpointConfig::class.java).autowire()
|
||||||
|
this.mockMvc.get("/saml/metadata")
|
||||||
|
.andExpect {
|
||||||
|
status { isOk() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
open class Saml2LoginCustomEndpointConfig {
|
||||||
|
@Bean
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
saml2Metadata {
|
||||||
|
metadataUrl = "/saml/metadata"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun relyingPartyRegistrationRepository(): RelyingPartyRegistrationRepository? {
|
||||||
|
return InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.full().build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `metadata endpoint when resolver customized then used`() {
|
||||||
|
this.spring.register(Saml2LoginCustomMetadataResolverConfig::class.java).autowire()
|
||||||
|
val mocked = this.spring.context.getBean(Saml2MetadataResponseResolver::class.java)
|
||||||
|
every {
|
||||||
|
mocked.resolve(any())
|
||||||
|
} returns Saml2MetadataResponse("metadata", "file")
|
||||||
|
this.mockMvc.get("/saml2/metadata")
|
||||||
|
.andExpect {
|
||||||
|
status { isOk() }
|
||||||
|
}
|
||||||
|
verify(exactly = 1) { mocked.resolve(any()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
open class Saml2LoginCustomMetadataResolverConfig {
|
||||||
|
|
||||||
|
private val metadataResponseResolver: Saml2MetadataResponseResolver = mockk()
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
saml2Metadata {}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun metadataResponseResolver(): Saml2MetadataResponseResolver? {
|
||||||
|
return this.metadataResponseResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun relyingPartyRegistrationRepository(): RelyingPartyRegistrationRepository? {
|
||||||
|
return InMemoryRelyingPartyRegistrationRepository(TestRelyingPartyRegistrations.full().build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,38 +32,25 @@ val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor();
|
||||||
[[publishing-relying-party-metadata]]
|
[[publishing-relying-party-metadata]]
|
||||||
== Producing `<saml2:SPSSODescriptor>` Metadata
|
== Producing `<saml2:SPSSODescriptor>` Metadata
|
||||||
|
|
||||||
You can publish a metadata endpoint by adding the `Saml2MetadataFilter` to the filter chain, as you'll see below:
|
You can publish a metadata endpoint using the `saml2Metadata` DSL method, as you'll see below:
|
||||||
|
|
||||||
====
|
====
|
||||||
.Java
|
.Java
|
||||||
[source,java,role="primary"]
|
[source,java,role="primary"]
|
||||||
----
|
----
|
||||||
DefaultRelyingPartyRegistrationResolver relyingPartyRegistrationResolver =
|
|
||||||
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository);
|
|
||||||
Saml2MetadataFilter filter = new Saml2MetadataFilter(
|
|
||||||
relyingPartyRegistrationResolver,
|
|
||||||
new OpenSamlMetadataResolver());
|
|
||||||
|
|
||||||
http
|
http
|
||||||
// ...
|
// ...
|
||||||
.saml2Login(withDefaults())
|
.saml2Login(withDefaults())
|
||||||
.addFilterBefore(filter, Saml2WebSsoAuthenticationFilter.class);
|
.saml2Metadata(withDefaults());
|
||||||
----
|
----
|
||||||
|
|
||||||
.Kotlin
|
.Kotlin
|
||||||
[source,kotlin,role="secondary"]
|
[source,kotlin,role="secondary"]
|
||||||
----
|
----
|
||||||
val relyingPartyRegistrationResolver: Converter<HttpServletRequest, RelyingPartyRegistration> =
|
|
||||||
DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository)
|
|
||||||
val filter = Saml2MetadataFilter(
|
|
||||||
relyingPartyRegistrationResolver,
|
|
||||||
OpenSamlMetadataResolver()
|
|
||||||
)
|
|
||||||
|
|
||||||
http {
|
http {
|
||||||
//...
|
//...
|
||||||
saml2Login { }
|
saml2Login { }
|
||||||
addFilterBefore<Saml2WebSsoAuthenticationFilter>(filter)
|
saml2Metadata { }
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
@ -71,77 +58,52 @@ http {
|
||||||
You can use this metadata endpoint to register your relying party with your asserting party.
|
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.
|
This is often as simple as finding the correct form field to supply the metadata endpoint.
|
||||||
|
|
||||||
By default, the metadata endpoint is `+/saml2/service-provider-metadata/{registrationId}+`.
|
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 `setRequestMatcher` method on the filter:
|
|
||||||
|
You can change this by calling the `metadataUrl` method in the DSL:
|
||||||
|
|
||||||
====
|
====
|
||||||
.Java
|
.Java
|
||||||
[source,java,role="primary"]
|
[source,java,role="primary"]
|
||||||
----
|
----
|
||||||
filter.setRequestMatcher(new AntPathRequestMatcher("/saml2/metadata/{registrationId}", "GET"));
|
.saml2Metadata((saml2) -> saml2.metadataUrl("/saml/metadata"))
|
||||||
----
|
----
|
||||||
|
|
||||||
.Kotlin
|
.Kotlin
|
||||||
[source,kotlin,role="secondary"]
|
[source,kotlin,role="secondary"]
|
||||||
----
|
----
|
||||||
filter.setRequestMatcher(AntPathRequestMatcher("/saml2/metadata/{registrationId}", "GET"))
|
saml2Metadata {
|
||||||
----
|
metadataUrl = "/saml/metadata"
|
||||||
====
|
}
|
||||||
|
|
||||||
Or, if you have registered a custom relying party registration resolver in the constructor, then you can specify a path without a `registrationId` hint, like so:
|
|
||||||
|
|
||||||
====
|
|
||||||
.Java
|
|
||||||
[source,java,role="primary"]
|
|
||||||
----
|
|
||||||
filter.setRequestMatcher(new AntPathRequestMatcher("/saml2/metadata", "GET"));
|
|
||||||
----
|
|
||||||
|
|
||||||
.Kotlin
|
|
||||||
[source,kotlin,role="secondary"]
|
|
||||||
----
|
|
||||||
filter.setRequestMatcher(AntPathRequestMatcher("/saml2/metadata", "GET"))
|
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
== Changing the Way a `RelyingPartyRegistration` Is Looked Up
|
== Changing the Way a `RelyingPartyRegistration` Is Looked Up
|
||||||
|
|
||||||
To apply a custom `RelyingPartyRegistrationResolver` to the metadata endpoint, you can provide it directly in the filter constructor like so:
|
If you have a different strategy for identifying which `RelyingPartyRegistration` to use, you can configure your own `Saml2MetadataResponseResolver` like the one below:
|
||||||
|
|
||||||
====
|
====
|
||||||
.Java
|
.Java
|
||||||
[source,java,role="primary"]
|
[source,java,role="primary"]
|
||||||
----
|
----
|
||||||
RelyingPartyRegistrationResolver myRegistrationResolver = ...;
|
@Bean
|
||||||
Saml2MetadataFilter metadata = new Saml2MetadataFilter(myRegistrationResolver, new OpenSamlMetadataResolver());
|
Saml2MetadataResponseResolver metadataResponseResolver(RelyingPartyRegistrationRepository registrations) {
|
||||||
|
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(
|
||||||
// ...
|
(id) -> registrations.findByRegistrationId("relying-party"));
|
||||||
|
metadata.setMetadataFilename("metadata.xml");
|
||||||
http.addFilterBefore(metadata, BasicAuthenticationFilter.class);
|
return metadata;
|
||||||
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
.Kotlin
|
.Kotlin
|
||||||
|
[source,kotlin,role="secondary"]
|
||||||
----
|
----
|
||||||
val myRegistrationResolver: RelyingPartyRegistrationResolver = ...;
|
@Bean
|
||||||
val metadata = new Saml2MetadataFilter(myRegistrationResolver, OpenSamlMetadataResolver());
|
fun metadataResponseResolver(val registrations: RelyingPartyRegistrationRepository): Saml2MetadataResponseResolver {
|
||||||
|
val metadata = new RequestMatcherMetadataResponseResolver(
|
||||||
// ...
|
id: String -> registrations.findByRegistrationId("relying-party"))
|
||||||
|
metadata.setMetadataFilename("metadata.xml")
|
||||||
http.addFilterBefore(metadata, BasicAuthenticationFilter::class.java);
|
return metadata
|
||||||
----
|
}
|
||||||
====
|
|
||||||
|
|
||||||
In the event that you are applying a `RelyingPartyRegistrationResolver` to remove the `registrationId` from the URI, you must also change the URI in the filter like so:
|
|
||||||
|
|
||||||
====
|
|
||||||
.Java
|
|
||||||
[source,java,role="primary"]
|
|
||||||
----
|
|
||||||
metadata.setRequestMatcher("/saml2/metadata")
|
|
||||||
----
|
|
||||||
|
|
||||||
.Kotlin
|
|
||||||
----
|
|
||||||
metadata.setRequestMatcher("/saml2/metadata")
|
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
Loading…
Reference in New Issue