Skip to content

Commit c730ab7

Browse files
committed
Merge branch '2.7.x' into main
2 parents b35006f + 7d459a1 commit c730ab7

File tree

6 files changed

+139
-0
lines changed

6 files changed

+139
-0
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/saml2/Saml2LoginConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class Saml2LoginConfiguration {
3838
@Bean
3939
SecurityFilterChain samlSecurityFilterChain(HttpSecurity http) throws Exception {
4040
http.authorizeRequests((requests) -> requests.anyRequest().authenticated()).saml2Login();
41+
http.saml2Logout();
4142
return http.build();
4243
}
4344

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/saml2/Saml2RelyingPartyProperties.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ public static class Registration {
6565

6666
private final Decryption decryption = new Decryption();
6767

68+
private final Singlelogout singlelogout = new Singlelogout();
69+
6870
/**
6971
* Remote SAML Identity Provider.
7072
*/
@@ -94,6 +96,10 @@ public AssertingParty getAssertingparty() {
9496
return this.assertingparty;
9597
}
9698

99+
public Singlelogout getSinglelogout() {
100+
return this.singlelogout;
101+
}
102+
97103
public static class Acs {
98104

99105
/**
@@ -241,6 +247,8 @@ public static class AssertingParty {
241247

242248
private final Verification verification = new Verification();
243249

250+
private final Singlelogout singlelogout = new Singlelogout();
251+
244252
public String getEntityId() {
245253
return this.entityId;
246254
}
@@ -265,6 +273,10 @@ public Verification getVerification() {
265273
return this.verification;
266274
}
267275

276+
public Singlelogout getSinglelogout() {
277+
return this.singlelogout;
278+
}
279+
268280
/**
269281
* Single sign on details for an Identity Provider.
270282
*/
@@ -351,4 +363,50 @@ public void setCertificateLocation(Resource certificate) {
351363

352364
}
353365

366+
/**
367+
* Single logout details.
368+
*/
369+
public static class Singlelogout {
370+
371+
/**
372+
* Location where SAML2 LogoutRequest gets sent to.
373+
*/
374+
private String url;
375+
376+
/**
377+
* Location where SAML2 LogoutResponse gets sent to.
378+
*/
379+
private String responseUrl;
380+
381+
/**
382+
* Whether to redirect or post logout requests.
383+
*/
384+
private Saml2MessageBinding binding;
385+
386+
public String getUrl() {
387+
return this.url;
388+
}
389+
390+
public void setUrl(String url) {
391+
this.url = url;
392+
}
393+
394+
public String getResponseUrl() {
395+
return this.responseUrl;
396+
}
397+
398+
public void setResponseUrl(String responseUrl) {
399+
this.responseUrl = responseUrl;
400+
}
401+
402+
public Saml2MessageBinding getBinding() {
403+
return this.binding;
404+
}
405+
406+
public void setBinding(Saml2MessageBinding binding) {
407+
this.binding = binding;
408+
}
409+
410+
}
411+
354412
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/saml2/Saml2RelyingPartyRegistrationConfiguration.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ private RelyingPartyRegistration asRegistration(String id, Registration properti
8787
builder.assertingPartyDetails((details) -> details
8888
.verificationX509Credentials((credentials) -> properties.getAssertingparty().getVerification()
8989
.getCredentials().stream().map(this::asVerificationCredential).forEach(credentials::add)));
90+
builder.singleLogoutServiceLocation(properties.getSinglelogout().getUrl());
91+
builder.singleLogoutServiceResponseLocation(properties.getSinglelogout().getResponseUrl());
92+
builder.singleLogoutServiceBinding(properties.getSinglelogout().getBinding());
9093
builder.entityId(properties.getEntityId());
9194
RelyingPartyRegistration registration = builder.build();
9295
boolean signRequest = registration.getAssertingPartyDetails().getWantAuthnRequestsSigned();
@@ -103,6 +106,9 @@ private Consumer<AssertingPartyDetails.Builder> mapAssertingParty(AssertingParty
103106
map.from(assertingParty.getSinglesignon()::getUrl).to(details::singleSignOnServiceLocation);
104107
map.from(assertingParty.getSinglesignon()::isSignRequest).when((signRequest) -> !usingMetadata)
105108
.to(details::wantAuthnRequestsSigned);
109+
map.from(assertingParty.getSinglelogout()::getUrl).to(details::singleLogoutServiceLocation);
110+
map.from(assertingParty.getSinglelogout()::getResponseUrl).to(details::singleLogoutServiceResponseLocation);
111+
map.from(assertingParty.getSinglelogout()::getBinding).to(details::singleLogoutServiceBinding);
106112
};
107113
}
108114

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/saml2/Saml2RelyingPartyAutoConfigurationTests.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
4343
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
4444
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
45+
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
4546
import org.springframework.security.web.FilterChainProxy;
4647
import org.springframework.security.web.SecurityFilterChain;
4748

@@ -102,6 +103,17 @@ void relyingPartyRegistrationRepositoryBeanShouldBeCreatedWhenPropertiesPresent(
102103
assertThat(registration.getDecryptionX509Credentials()).hasSize(1);
103104
assertThat(registration.getAssertingPartyDetails().getVerificationX509Credentials()).isNotNull();
104105
assertThat(registration.getEntityId()).isEqualTo("{baseUrl}/saml2/foo-entity-id");
106+
assertThat(registration.getSingleLogoutServiceLocation())
107+
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php");
108+
assertThat(registration.getSingleLogoutServiceResponseLocation())
109+
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/");
110+
assertThat(registration.getSingleLogoutServiceBinding()).isEqualTo(Saml2MessageBinding.POST);
111+
assertThat(registration.getAssertingPartyDetails().getSingleLogoutServiceLocation())
112+
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php");
113+
assertThat(registration.getAssertingPartyDetails().getSingleLogoutServiceResponseLocation())
114+
.isEqualTo("https://simplesaml-for-spring-saml.cfapps.io/");
115+
assertThat(registration.getAssertingPartyDetails().getSingleLogoutServiceBinding())
116+
.isEqualTo(Saml2MessageBinding.POST);
105117
});
106118
}
107119

@@ -214,6 +226,12 @@ void samlLoginShouldShouldBeConditionalOnSecurityWebFilterClass() {
214226
.run((context) -> assertThat(context).doesNotHaveBean(SecurityFilterChain.class));
215227
}
216228

229+
@Test
230+
void samlLogoutShouldBeConfigured() {
231+
this.contextRunner.withPropertyValues(getPropertyValues())
232+
.run((context) -> assertThat(hasFilter(context, Saml2LogoutRequestFilter.class)).isTrue());
233+
}
234+
217235
private String[] getPropertyValuesWithoutSigningCredentials(boolean signRequests) {
218236
return new String[] { PREFIX
219237
+ ".foo.assertingparty.singlesignon.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php",
@@ -237,11 +255,17 @@ private String[] getPropertyValues() {
237255
PREFIX + ".foo.signing.credentials[0].certificate-location=classpath:saml/certificate-location",
238256
PREFIX + ".foo.decryption.credentials[0].private-key-location=classpath:saml/private-key-location",
239257
PREFIX + ".foo.decryption.credentials[0].certificate-location=classpath:saml/certificate-location",
258+
PREFIX + ".foo.singlelogout.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php",
259+
PREFIX + ".foo.singlelogout.response-url=https://simplesaml-for-spring-saml.cfapps.io/",
260+
PREFIX + ".foo.singlelogout.binding=post",
240261
PREFIX + ".foo.assertingparty.singlesignon.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php",
241262
PREFIX + ".foo.assertingparty.singlesignon.binding=post",
242263
PREFIX + ".foo.assertingparty.singlesignon.sign-request=false",
243264
PREFIX + ".foo.assertingparty.entity-id=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php",
244265
PREFIX + ".foo.assertingparty.verification.credentials[0].certificate-location=classpath:saml/certificate-location",
266+
PREFIX + ".foo.asserting-party.singlelogout.url=https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SLOService.php",
267+
PREFIX + ".foo.asserting-party.singlelogout.response-url=https://simplesaml-for-spring-saml.cfapps.io/",
268+
PREFIX + ".foo.asserting-party.singlelogout.binding=post",
245269
PREFIX + ".foo.entity-id={baseUrl}/saml2/foo-entity-id",
246270
PREFIX + ".foo.acs.location={baseUrl}/login/saml2/foo-entity-id",
247271
PREFIX + ".foo.acs.binding=redirect" };

spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-security.adoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ You can register multiple relying parties under the `spring.security.saml2.relyi
262262
credentials:
263263
- private-key-location: "path-to-private-key"
264264
certificate-location: "path-to-certificate"
265+
singlelogout:
266+
url: "https://myapp/logout/saml2/slo"
267+
reponse-url: "https://remoteidp2.slo.url"
268+
binding: "POST"
265269
assertingparty:
266270
verification:
267271
credentials:
@@ -284,4 +288,14 @@ You can register multiple relying parties under the `spring.security.saml2.relyi
284288
- certificate-location: "path-to-other-verification-cert"
285289
entity-id: "remote-idp-entity-id2"
286290
sso-url: "https://remoteidp2.sso.url"
291+
singlelogout:
292+
url: "https://remoteidp2.slo.url"
293+
reponse-url: "https://myapp/logout/saml2/slo"
294+
binding: "POST"
287295
----
296+
297+
For SAML2 logout, by default, Spring Security's `Saml2LogoutRequestFilter` and `Saml2LogoutResponseFilter` only process URLs matching `/logout/saml2/slo`.
298+
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.
299+
For example, for servlet applications, you can add your own `SecurityFilterChain` that resembles the following:
300+
301+
include::code:MySamlRelyingPartyConfiguration[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.web.security.saml2.relyingparty;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
22+
import org.springframework.security.web.SecurityFilterChain;
23+
24+
@Configuration(proxyBeanMethods = false)
25+
public class MySamlRelyingPartyConfiguration {
26+
27+
@Bean
28+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
29+
http.authorizeRequests().anyRequest().authenticated();
30+
http.saml2Login();
31+
http.saml2Logout((saml2) -> saml2.logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
32+
.logoutResponse((response) -> response.logoutUrl("/SLOService.saml2")));
33+
return http.build();
34+
}
35+
36+
}

0 commit comments

Comments
 (0)