Merge branch '2.7.x' into main
This commit is contained in:
commit
c730ab7d0c
|
@ -38,6 +38,7 @@ class Saml2LoginConfiguration {
|
|||
@Bean
|
||||
SecurityFilterChain samlSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests((requests) -> requests.anyRequest().authenticated()).saml2Login();
|
||||
http.saml2Logout();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ public class Saml2RelyingPartyProperties {
|
|||
|
||||
private final Decryption decryption = new Decryption();
|
||||
|
||||
private final Singlelogout singlelogout = new Singlelogout();
|
||||
|
||||
/**
|
||||
* Remote SAML Identity Provider.
|
||||
*/
|
||||
|
@ -94,6 +96,10 @@ public class Saml2RelyingPartyProperties {
|
|||
return this.assertingparty;
|
||||
}
|
||||
|
||||
public Singlelogout getSinglelogout() {
|
||||
return this.singlelogout;
|
||||
}
|
||||
|
||||
public static class Acs {
|
||||
|
||||
/**
|
||||
|
@ -241,6 +247,8 @@ public class Saml2RelyingPartyProperties {
|
|||
|
||||
private final Verification verification = new Verification();
|
||||
|
||||
private final Singlelogout singlelogout = new Singlelogout();
|
||||
|
||||
public String getEntityId() {
|
||||
return this.entityId;
|
||||
}
|
||||
|
@ -265,6 +273,10 @@ public class Saml2RelyingPartyProperties {
|
|||
return this.verification;
|
||||
}
|
||||
|
||||
public Singlelogout getSinglelogout() {
|
||||
return this.singlelogout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single sign on details for an Identity Provider.
|
||||
*/
|
||||
|
@ -351,4 +363,50 @@ public class Saml2RelyingPartyProperties {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Single logout details.
|
||||
*/
|
||||
public static class Singlelogout {
|
||||
|
||||
/**
|
||||
* Location where SAML2 LogoutRequest gets sent to.
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* Location where SAML2 LogoutResponse gets sent to.
|
||||
*/
|
||||
private String responseUrl;
|
||||
|
||||
/**
|
||||
* Whether to redirect or post logout requests.
|
||||
*/
|
||||
private Saml2MessageBinding binding;
|
||||
|
||||
public String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getResponseUrl() {
|
||||
return this.responseUrl;
|
||||
}
|
||||
|
||||
public void setResponseUrl(String responseUrl) {
|
||||
this.responseUrl = responseUrl;
|
||||
}
|
||||
|
||||
public Saml2MessageBinding getBinding() {
|
||||
return this.binding;
|
||||
}
|
||||
|
||||
public void setBinding(Saml2MessageBinding binding) {
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -87,6 +87,9 @@ class Saml2RelyingPartyRegistrationConfiguration {
|
|||
builder.assertingPartyDetails((details) -> details
|
||||
.verificationX509Credentials((credentials) -> properties.getAssertingparty().getVerification()
|
||||
.getCredentials().stream().map(this::asVerificationCredential).forEach(credentials::add)));
|
||||
builder.singleLogoutServiceLocation(properties.getSinglelogout().getUrl());
|
||||
builder.singleLogoutServiceResponseLocation(properties.getSinglelogout().getResponseUrl());
|
||||
builder.singleLogoutServiceBinding(properties.getSinglelogout().getBinding());
|
||||
builder.entityId(properties.getEntityId());
|
||||
RelyingPartyRegistration registration = builder.build();
|
||||
boolean signRequest = registration.getAssertingPartyDetails().getWantAuthnRequestsSigned();
|
||||
|
@ -103,6 +106,9 @@ class Saml2RelyingPartyRegistrationConfiguration {
|
|||
map.from(assertingParty.getSinglesignon()::getUrl).to(details::singleSignOnServiceLocation);
|
||||
map.from(assertingParty.getSinglesignon()::isSignRequest).when((signRequest) -> !usingMetadata)
|
||||
.to(details::wantAuthnRequestsSigned);
|
||||
map.from(assertingParty.getSinglelogout()::getUrl).to(details::singleLogoutServiceLocation);
|
||||
map.from(assertingParty.getSinglelogout()::getResponseUrl).to(details::singleLogoutServiceResponseLocation);
|
||||
map.from(assertingParty.getSinglelogout()::getBinding).to(details::singleLogoutServiceBinding);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
|
|||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
|
@ -102,6 +103,17 @@ class Saml2RelyingPartyAutoConfigurationTests {
|
|||
assertThat(registration.getDecryptionX509Credentials()).hasSize(1);
|
||||
assertThat(registration.getAssertingPartyDetails().getVerificationX509Credentials()).isNotNull();
|
||||
assertThat(registration.getEntityId()).isEqualTo("{baseUrl}/saml2/foo-entity-id");
|
||||
assertThat(registration.getSingleLogoutServiceLocation())
|
||||
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php");
|
||||
assertThat(registration.getSingleLogoutServiceResponseLocation())
|
||||
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/");
|
||||
assertThat(registration.getSingleLogoutServiceBinding()).isEqualTo(Saml2MessageBinding.POST);
|
||||
assertThat(registration.getAssertingPartyDetails().getSingleLogoutServiceLocation())
|
||||
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php");
|
||||
assertThat(registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation())
|
||||
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/");
|
||||
assertThat(registration.getAssertingPartyDetails().getSingleLogoutServiceBinding())
|
||||
.isEqualTo(Saml2MessageBinding.POST);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -214,6 +226,12 @@ class Saml2RelyingPartyAutoConfigurationTests {
|
|||
.run((context) -> assertThat(context).doesNotHaveBean(SecurityFilterChain.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void samlLogoutShouldBeConfigured() {
|
||||
this.contextRunner.withPropertyValues(getPropertyValues())
|
||||
.run((context) -> assertThat(hasFilter(context, Saml2LogoutRequestFilter.class)).isTrue());
|
||||
}
|
||||
|
||||
private String[] getPropertyValuesWithoutSigningCredentials(boolean signRequests) {
|
||||
return new String[] { PREFIX
|
||||
+ ".foo.assertingparty.singlesignon.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php",
|
||||
|
@ -237,11 +255,17 @@ class Saml2RelyingPartyAutoConfigurationTests {
|
|||
PREFIX + ".foo.signing.credentials[0].certificate-location=classpath:saml/certificate-location",
|
||||
PREFIX + ".foo.decryption.credentials[0].private-key-location=classpath:saml/private-key-location",
|
||||
PREFIX + ".foo.decryption.credentials[0].certificate-location=classpath:saml/certificate-location",
|
||||
PREFIX + ".foo.singlelogout.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php",
|
||||
PREFIX + ".foo.singlelogout.response-url=https://simplesaml-for-spring-saml.cfapps.io/",
|
||||
PREFIX + ".foo.singlelogout.binding=post",
|
||||
PREFIX + ".foo.assertingparty.singlesignon.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php",
|
||||
PREFIX + ".foo.assertingparty.singlesignon.binding=post",
|
||||
PREFIX + ".foo.assertingparty.singlesignon.sign-request=false",
|
||||
PREFIX + ".foo.assertingparty.entity-id=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php",
|
||||
PREFIX + ".foo.assertingparty.verification.credentials[0].certificate-location=classpath:saml/certificate-location",
|
||||
PREFIX + ".foo.asserting-party.singlelogout.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php",
|
||||
PREFIX + ".foo.asserting-party.singlelogout.response-url=https://simplesaml-for-spring-saml.cfapps.io/",
|
||||
PREFIX + ".foo.asserting-party.singlelogout.binding=post",
|
||||
PREFIX + ".foo.entity-id={baseUrl}/saml2/foo-entity-id",
|
||||
PREFIX + ".foo.acs.location={baseUrl}/login/saml2/foo-entity-id",
|
||||
PREFIX + ".foo.acs.binding=redirect" };
|
||||
|
|
|
@ -262,6 +262,10 @@ You can register multiple relying parties under the `spring.security.saml2.relyi
|
|||
credentials:
|
||||
- private-key-location: "path-to-private-key"
|
||||
certificate-location: "path-to-certificate"
|
||||
singlelogout:
|
||||
url: "https://myapp/logout/saml2/slo"
|
||||
reponse-url: "https://remoteidp2.slo.url"
|
||||
binding: "POST"
|
||||
assertingparty:
|
||||
verification:
|
||||
credentials:
|
||||
|
@ -284,4 +288,14 @@ You can register multiple relying parties under the `spring.security.saml2.relyi
|
|||
- certificate-location: "path-to-other-verification-cert"
|
||||
entity-id: "remote-idp-entity-id2"
|
||||
sso-url: "https://remoteidp2.sso.url"
|
||||
singlelogout:
|
||||
url: "https://remoteidp2.slo.url"
|
||||
reponse-url: "https://myapp/logout/saml2/slo"
|
||||
binding: "POST"
|
||||
----
|
||||
|
||||
For SAML2 logout, by default, Spring Security's `Saml2LogoutRequestFilter` and `Saml2LogoutResponseFilter` only process URLs matching `/logout/saml2/slo`.
|
||||
If you want to customize the `url` to which AP-initiated logout requests get sent to or the `response-url` to which an AP sends logout responses to, to use a different pattern, you need to provide configuration to process that custom pattern.
|
||||
For example, for servlet applications, you can add your own `SecurityFilterChain` that resembles the following:
|
||||
|
||||
include::code:MySamlRelyingPartyConfiguration[]
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2012-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.boot.docs.web.security.saml2.relyingparty;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class MySamlRelyingPartyConfiguration {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().authenticated();
|
||||
http.saml2Login();
|
||||
http.saml2Logout((saml2) -> saml2.logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
|
||||
.logoutResponse((response) -> response.logoutUrl("/SLOService.saml2")));
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue