Skip to content

Commit 597cdf8

Browse files
committed
Add Saml2AuthenticationRequestResolver
1 parent 9dd2913 commit 597cdf8

File tree

13 files changed

+1265
-53
lines changed

13 files changed

+1265
-53
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
8383
this.filterToOrder.put(
8484
"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
8585
order.next());
86+
this.filterToOrder.put(
87+
"org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestRedirectFilter",
88+
order.next());
8689
put(X509AuthenticationFilter.class, order.next());
8790
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
8891
this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());

config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java

Lines changed: 98 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import javax.servlet.Filter;
2323

24+
import org.joda.time.DateTime;
25+
2426
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2527
import org.springframework.context.ApplicationContext;
2628
import org.springframework.security.authentication.AuthenticationManager;
@@ -41,6 +43,9 @@
4143
import org.springframework.security.saml2.provider.service.web.DefaultSaml2AuthenticationRequestContextResolver;
4244
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
4345
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
46+
import org.springframework.security.saml2.provider.service.web.authentication.OpenSamlAuthenticationRequestResolver;
47+
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestRedirectFilter;
48+
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
4449
import org.springframework.security.web.authentication.AuthenticationConverter;
4550
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
4651
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
@@ -49,6 +54,8 @@
4954
import org.springframework.util.Assert;
5055
import org.springframework.util.StringUtils;
5156

57+
import static org.springframework.security.saml2.provider.service.web.authentication.OpenSamlAuthenticationRequestResolver.OpenSamlAuthenticationRequestPartial;
58+
5259
/**
5360
* An {@link AbstractHttpConfigurer} for SAML 2.0 Login, which leverages the SAML 2.0 Web
5461
* Browser Single Sign On (WebSSO) Flow.
@@ -106,9 +113,11 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
106113

107114
private String loginPage;
108115

109-
private String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
116+
private String authenticationRequestUri = "/saml2/authenticate/{registrationId}";
110117

111-
private AuthenticationRequestEndpointConfig authenticationRequestEndpoint = new AuthenticationRequestEndpointConfig();
118+
private Saml2AuthenticationRequestResolver authenticationRequestResolver;
119+
120+
private String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
112121

113122
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
114123

@@ -167,6 +176,20 @@ public Saml2LoginConfigurer<B> loginPage(String loginPage) {
167176
return this;
168177
}
169178

179+
/**
180+
* Use this {@link Saml2AuthenticationRequestResolver} for generating SAML 2.0
181+
* Authentication Requests.
182+
* @param authenticationRequestResolver
183+
* @return the {@link Saml2LoginConfigurer} for further configuration
184+
* @since 5.5
185+
*/
186+
public Saml2LoginConfigurer<B> authenticationRequestResolver(
187+
Saml2AuthenticationRequestResolver authenticationRequestResolver) {
188+
Assert.notNull(authenticationRequestResolver, "authenticationRequestResolver cannot be null");
189+
this.authenticationRequestResolver = authenticationRequestResolver;
190+
return this;
191+
}
192+
170193
@Override
171194
public Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
172195
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty");
@@ -182,7 +205,7 @@ protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingU
182205

183206
/**
184207
* {@inheritDoc}
185-
*
208+
* <p>
186209
* Initializes this filter chain for SAML 2 Login. The following actions are taken:
187210
* <ul>
188211
* <li>The WebSSO endpoint has CSRF disabled, typically {@code /login/saml2/sso}</li>
@@ -209,8 +232,8 @@ public void init(B http) throws Exception {
209232
super.init(http);
210233
}
211234
else {
212-
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(
213-
this.authenticationRequestEndpoint.filterProcessingUrl, this.relyingPartyRegistrationRepository);
235+
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(this.authenticationRequestUri,
236+
this.relyingPartyRegistrationRepository);
214237
boolean singleProvider = providerUrlMap.size() == 1;
215238
if (singleProvider) {
216239
// Setup auto-redirect to provider login page
@@ -230,14 +253,25 @@ public void init(B http) throws Exception {
230253

231254
/**
232255
* {@inheritDoc}
233-
*
256+
* <p>
234257
* During the {@code configure} phase, a
235258
* {@link Saml2WebSsoAuthenticationRequestFilter} is added to handle SAML 2.0
236259
* AuthNRequest redirects
237260
*/
238261
@Override
239262
public void configure(B http) throws Exception {
240-
http.addFilter(this.authenticationRequestEndpoint.build(http));
263+
if (declaredAuthenticationRequestRedirectComponents(http)) {
264+
http.addFilter(getAuthenticationRequestRedirectFilter(http));
265+
}
266+
else if (declaredWebSsoAuthenticationRequestComponents(http)) {
267+
http.addFilter(postProcess(getWebSsoAuthenticationRequestFilter(http)));
268+
}
269+
else {
270+
Saml2WebSsoAuthenticationRequestFilter filter = getWebSsoAuthenticationRequestFilter(http);
271+
filter.setRedirectMatcher((request) -> false);
272+
http.addFilter(postProcess(filter));
273+
http.addFilter(getAuthenticationRequestRedirectFilter(http));
274+
}
241275
super.configure(http);
242276
if (this.authenticationManager == null) {
243277
registerDefaultAuthenticationProvider(http);
@@ -247,6 +281,61 @@ public void configure(B http) throws Exception {
247281
}
248282
}
249283

284+
private boolean declaredWebSsoAuthenticationRequestComponents(B http) {
285+
boolean declaredContextResolver = getBeanOrNull(http, Saml2AuthenticationRequestContextResolver.class) != null;
286+
boolean declaredFactory = getBeanOrNull(http, Saml2AuthenticationRequestFactory.class) != null;
287+
return (declaredContextResolver || declaredFactory);
288+
}
289+
290+
private boolean declaredAuthenticationRequestRedirectComponents(B http) {
291+
boolean declaredResolver = this.authenticationRequestResolver != null
292+
|| getBeanOrNull(http, Saml2AuthenticationRequestResolver.class) != null;
293+
return declaredResolver;
294+
}
295+
296+
private Saml2WebSsoAuthenticationRequestFilter getWebSsoAuthenticationRequestFilter(B http) {
297+
return new Saml2WebSsoAuthenticationRequestFilter(getAuthenticationRequestContextResolver(http),
298+
getAuthenticationRequestFactory(http));
299+
}
300+
301+
private Filter getAuthenticationRequestRedirectFilter(B http) {
302+
if (this.authenticationRequestResolver != null) {
303+
return new Saml2AuthenticationRequestRedirectFilter(this.authenticationRequestResolver);
304+
}
305+
this.authenticationRequestResolver = getBeanOrNull(http, Saml2AuthenticationRequestResolver.class);
306+
if (authenticationRequestResolver != null) {
307+
return new Saml2AuthenticationRequestRedirectFilter(authenticationRequestResolver);
308+
}
309+
OpenSamlAuthenticationRequestResolver delegate = new OpenSamlAuthenticationRequestResolver(
310+
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository));
311+
this.authenticationRequestResolver = (request) -> {
312+
OpenSamlAuthenticationRequestPartial partial = delegate.resolveAuthenticationRequest(request);
313+
if (partial == null) {
314+
return null;
315+
}
316+
return partial.authnRequest((authnRequest) -> authnRequest.setIssueInstant(DateTime.now()));
317+
};
318+
return new Saml2AuthenticationRequestRedirectFilter(this.authenticationRequestResolver);
319+
}
320+
321+
private Saml2AuthenticationRequestFactory getAuthenticationRequestFactory(B http) {
322+
Saml2AuthenticationRequestFactory resolver = getSharedOrBean(http, Saml2AuthenticationRequestFactory.class);
323+
if (resolver != null) {
324+
return resolver;
325+
}
326+
return new OpenSamlAuthenticationRequestFactory();
327+
}
328+
329+
private Saml2AuthenticationRequestContextResolver getAuthenticationRequestContextResolver(B http) {
330+
Saml2AuthenticationRequestContextResolver resolver = getBeanOrNull(http,
331+
Saml2AuthenticationRequestContextResolver.class);
332+
if (resolver != null) {
333+
return resolver;
334+
}
335+
return new DefaultSaml2AuthenticationRequestContextResolver(
336+
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository));
337+
}
338+
250339
private AuthenticationConverter getAuthenticationConverter(B http) {
251340
if (this.authenticationConverter == null) {
252341
return new Saml2AuthenticationTokenConverter(
@@ -275,8 +364,8 @@ private void initDefaultLoginFilter(B http) {
275364
return;
276365
}
277366
loginPageGeneratingFilter.setSaml2LoginEnabled(true);
278-
loginPageGeneratingFilter.setSaml2AuthenticationUrlToProviderName(this.getIdentityProviderUrlMap(
279-
this.authenticationRequestEndpoint.filterProcessingUrl, this.relyingPartyRegistrationRepository));
367+
loginPageGeneratingFilter.setSaml2AuthenticationUrlToProviderName(
368+
this.getIdentityProviderUrlMap(this.authenticationRequestUri, this.relyingPartyRegistrationRepository));
280369
loginPageGeneratingFilter.setLoginPageUrl(this.getLoginPage());
281370
loginPageGeneratingFilter.setFailureUrl(this.getFailureUrl());
282371
}
@@ -320,38 +409,4 @@ private <C> void setSharedObject(B http, Class<C> clazz, C object) {
320409
}
321410
}
322411

323-
private final class AuthenticationRequestEndpointConfig {
324-
325-
private String filterProcessingUrl = "/saml2/authenticate/{registrationId}";
326-
327-
private AuthenticationRequestEndpointConfig() {
328-
}
329-
330-
private Filter build(B http) {
331-
Saml2AuthenticationRequestFactory authenticationRequestResolver = getResolver(http);
332-
Saml2AuthenticationRequestContextResolver contextResolver = getContextResolver(http);
333-
return postProcess(
334-
new Saml2WebSsoAuthenticationRequestFilter(contextResolver, authenticationRequestResolver));
335-
}
336-
337-
private Saml2AuthenticationRequestFactory getResolver(B http) {
338-
Saml2AuthenticationRequestFactory resolver = getSharedOrBean(http, Saml2AuthenticationRequestFactory.class);
339-
if (resolver == null) {
340-
resolver = new OpenSamlAuthenticationRequestFactory();
341-
}
342-
return resolver;
343-
}
344-
345-
private Saml2AuthenticationRequestContextResolver getContextResolver(B http) {
346-
Saml2AuthenticationRequestContextResolver resolver = getBeanOrNull(http,
347-
Saml2AuthenticationRequestContextResolver.class);
348-
if (resolver == null) {
349-
return new DefaultSaml2AuthenticationRequestContextResolver(new DefaultRelyingPartyRegistrationResolver(
350-
Saml2LoginConfigurer.this.relyingPartyRegistrationRepository));
351-
}
352-
return resolver;
353-
}
354-
355-
}
356-
357412
}

0 commit comments

Comments
 (0)