Browse Source

Redirect to Appropriate Entry Point Based on Missing Authorities

Issue gh-17934
pull/17966/head
Josh Cummings 3 months ago
parent
commit
e66c498d80
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
  1. 233
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurer.java
  2. 4
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.java
  3. 10
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java
  4. 3
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/X509Configurer.java
  5. 5
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java
  6. 4
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java
  7. 67
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurer.java
  8. 5
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java
  9. 3
      web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java

233
config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurer.java

@ -19,9 +19,8 @@ package org.springframework.security.config.annotation.web.configurers; @@ -19,9 +19,8 @@ package org.springframework.security.config.annotation.web.configurers;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@ -35,29 +34,22 @@ import org.springframework.security.authorization.AuthorizationDeniedException; @@ -35,29 +34,22 @@ import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.FormPostRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.RequestMatcherDelegatingAccessDeniedHandler;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Adds exception handling for Spring Security related exceptions to an application. All
@ -102,6 +94,8 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> @@ -102,6 +94,8 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = new LinkedHashMap<>();
private Map<String, LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>> entryPoints = new LinkedHashMap<>();
/**
* Creates a new instance
* @see HttpSecurity#exceptionHandling(Customizer)
@ -195,6 +189,26 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> @@ -195,6 +189,26 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint,
RequestMatcher preferredMatcher, String authority) {
this.defaultEntryPointMappings.put(preferredMatcher, entryPoint);
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> byMatcher = this.entryPoints.get(authority);
if (byMatcher == null) {
byMatcher = new LinkedHashMap<>();
}
byMatcher.put(preferredMatcher, entryPoint);
this.entryPoints.put(authority, byMatcher);
return this;
}
public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint,
String authority) {
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> byMatcher = new LinkedHashMap<>();
byMatcher.put(AnyRequestMatcher.INSTANCE, entryPoint);
this.entryPoints.put(authority, byMatcher);
return this;
}
/**
* Gets any explicitly configured {@link AuthenticationEntryPoint}
* @return
@ -254,21 +268,60 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> @@ -254,21 +268,60 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
}
private AccessDeniedHandler createDefaultDeniedHandler(H http) {
AccessDeniedHandler defaults = createDefaultAccessDeniedHandler(http);
if (this.entryPoints.isEmpty()) {
return defaults;
}
Map<String, AccessDeniedHandler> deniedHandlers = new LinkedHashMap<>();
for (Map.Entry<String, LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>> entry : this.entryPoints
.entrySet()) {
AuthenticationEntryPoint entryPoint = entryPointFrom(entry.getValue());
AuthenticationEntryPointAccessDeniedHandlerAdapter deniedHandler = new AuthenticationEntryPointAccessDeniedHandlerAdapter(
entryPoint);
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache != null) {
deniedHandler.setRequestCache(requestCache);
}
deniedHandlers.put(entry.getKey(), deniedHandler);
}
return new AuthenticationFactorDelegatingAccessDeniedHandler(deniedHandlers, defaults);
}
private AccessDeniedHandler createDefaultAccessDeniedHandler(H http) {
if (this.defaultDeniedHandlerMappings.isEmpty()) {
return new AuthenticationFactorDelegatingAccessDeniedHandler();
return new AccessDeniedHandlerImpl();
}
if (this.defaultDeniedHandlerMappings.size() == 1) {
return this.defaultDeniedHandlerMappings.values().iterator().next();
}
return new RequestMatcherDelegatingAccessDeniedHandler(this.defaultDeniedHandlerMappings,
new AuthenticationFactorDelegatingAccessDeniedHandler());
new AccessDeniedHandlerImpl());
}
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
if (this.defaultEntryPoint == null) {
AuthenticationEntryPoint defaults = entryPointFrom(this.defaultEntryPointMappings);
if (this.entryPoints.isEmpty()) {
return defaults;
}
Map<String, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>();
for (Map.Entry<String, LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>> entry : this.entryPoints
.entrySet()) {
entryPoints.put(entry.getKey(), entryPointFrom(entry.getValue()));
}
return new AuthenticationFactorDelegatingAuthenticationEntryPoint(entryPoints, defaults);
}
private AuthenticationEntryPoint entryPointFrom(
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints) {
if (entryPoints.isEmpty()) {
return new Http403ForbiddenEntryPoint();
}
return this.defaultEntryPoint.build();
if (entryPoints.size() == 1) {
return entryPoints.values().iterator().next();
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
entryPoint.setDefaultEntryPoint(entryPoints.values().iterator().next());
return entryPoint;
}
/**
@ -287,94 +340,126 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> @@ -287,94 +340,126 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
return new HttpSessionRequestCache();
}
private static final class AuthenticationFactorDelegatingAccessDeniedHandler implements AccessDeniedHandler {
private static final class AuthenticationFactorDelegatingAuthenticationEntryPoint
implements AuthenticationEntryPoint {
private final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
private final Map<String, AuthenticationEntryPoint> entryPoints = Map.of("FACTOR_PASSWORD",
new LoginUrlAuthenticationEntryPoint("/login"), "FACTOR_AUTHORIZATION_CODE",
new LoginUrlAuthenticationEntryPoint("/login"), "FACTOR_SAML_RESPONSE",
new LoginUrlAuthenticationEntryPoint("/login"), "FACTOR_WEBAUTHN",
new LoginUrlAuthenticationEntryPoint("/login"), "FACTOR_BEARER",
new BearerTokenAuthenticationEntryPoint(), "FACTOR_OTT",
new PostAuthenticationEntryPoint(GenerateOneTimeTokenFilter.DEFAULT_GENERATE_URL + "?username={u}",
Map.of("u", Authentication::getName)));
private final Map<String, AuthenticationEntryPoint> entryPoints;
private final AccessDeniedHandler defaults = new AccessDeniedHandlerImpl();
private final AuthenticationEntryPoint defaults;
private AuthenticationFactorDelegatingAuthenticationEntryPoint(
Map<String, AuthenticationEntryPoint> entryPoints, AuthenticationEntryPoint defaults) {
this.entryPoints = new LinkedHashMap<>(entryPoints);
this.defaults = defaults;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex)
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex)
throws IOException, ServletException {
Collection<String> needed = authorizationRequest(ex);
if (needed == null) {
this.defaults.handle(request, response, ex);
return;
Collection<GrantedAuthority> authorization = authorizationRequest(ex);
entryPoint(authorization).commence(request, response, ex);
}
private AuthenticationEntryPoint entryPoint(Collection<GrantedAuthority> authorities) {
if (authorities == null) {
return this.defaults;
}
for (String authority : needed) {
AuthenticationEntryPoint entryPoint = this.entryPoints.get(authority);
for (GrantedAuthority needed : authorities) {
AuthenticationEntryPoint entryPoint = this.entryPoints.get(needed.getAuthority());
if (entryPoint != null) {
AuthenticationException insufficient = new InsufficientAuthenticationException(ex.getMessage(), ex);
entryPoint.commence(request, response, insufficient);
return;
return entryPoint;
}
}
this.defaults.handle(request, response, ex);
return this.defaults;
}
private Collection<String> authorizationRequest(AccessDeniedException access) {
if (!(access instanceof AuthorizationDeniedException denied)) {
return null;
private Collection<GrantedAuthority> authorizationRequest(Exception ex) {
Throwable[] chain = this.throwableAnalyzer.determineCauseChain(ex);
AuthorizationDeniedException denied = (AuthorizationDeniedException) this.throwableAnalyzer
.getFirstThrowableOfType(AuthorizationDeniedException.class, chain);
if (denied == null) {
return List.of();
}
if (!(denied.getAuthorizationResult() instanceof AuthorityAuthorizationDecision decision)) {
return null;
if (!(denied.getAuthorizationResult() instanceof AuthorityAuthorizationDecision authorization)) {
return List.of();
}
return decision.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
return authorization.getAuthorities();
}
}
private static final class PostAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final class AuthenticationEntryPointAccessDeniedHandlerAdapter implements AccessDeniedHandler {
private final AuthenticationEntryPoint entryPoint;
private RequestCache requestCache = new NullRequestCache();
private AuthenticationEntryPointAccessDeniedHandlerAdapter(AuthenticationEntryPoint entryPoint) {
this.entryPoint = entryPoint;
}
void setRequestCache(RequestCache requestCache) {
Assert.notNull(requestCache, "requestCache cannot be null");
this.requestCache = requestCache;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException denied)
throws IOException, ServletException {
AuthenticationException ex = new InsufficientAuthenticationException("access denied", denied);
this.requestCache.saveRequest(request, response);
this.entryPoint.commence(request, response, ex);
}
private final String entryPointUri;
}
private static final class AuthenticationFactorDelegatingAccessDeniedHandler implements AccessDeniedHandler {
private final Map<String, Function<Authentication, String>> params;
private final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
private final Map<String, AccessDeniedHandler> deniedHandlers;
private RedirectStrategy redirectStrategy = new FormPostRedirectStrategy();
private final AccessDeniedHandler defaults;
private PostAuthenticationEntryPoint(String entryPointUri,
Map<String, Function<Authentication, String>> params) {
this.entryPointUri = entryPointUri;
this.params = params;
private AuthenticationFactorDelegatingAccessDeniedHandler(Map<String, AccessDeniedHandler> deniedHandlers,
AccessDeniedHandler defaults) {
this.deniedHandlers = new LinkedHashMap<>(deniedHandlers);
this.defaults = defaults;
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
Authentication authentication = getAuthentication(authException);
Assert.notNull(authentication, "could not find authentication in order to perform post");
Map<String, String> params = this.params.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, (entry) -> entry.getValue().apply(authentication)));
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(this.entryPointUri);
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
builder.queryParam(csrf.getParameterName(), csrf.getToken());
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex)
throws IOException, ServletException {
Collection<GrantedAuthority> authorization = authorizationRequest(ex);
deniedHandler(authorization).handle(request, response, ex);
}
private AccessDeniedHandler deniedHandler(Collection<GrantedAuthority> authorities) {
if (authorities == null) {
return this.defaults;
}
for (GrantedAuthority needed : authorities) {
AccessDeniedHandler deniedHandler = this.deniedHandlers.get(needed.getAuthority());
if (deniedHandler != null) {
return deniedHandler;
}
}
String entryPointUrl = builder.build(false).expand(params).toUriString();
this.redirectStrategy.sendRedirect(request, response, entryPointUrl);
return this.defaults;
}
private Authentication getAuthentication(AuthenticationException authException) {
Authentication authentication = authException.getAuthenticationRequest();
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
private Collection<GrantedAuthority> authorizationRequest(Exception ex) {
Throwable[] chain = this.throwableAnalyzer.determineCauseChain(ex);
AuthorizationDeniedException denied = (AuthorizationDeniedException) this.throwableAnalyzer
.getFirstThrowableOfType(AuthorizationDeniedException.class, chain);
if (denied == null) {
return List.of();
}
authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
if (!(denied.getAuthorizationResult() instanceof AuthorityAuthorizationDecision authorization)) {
return List.of();
}
return null;
return authorization.getAuthorities();
}
}

4
config/src/main/java/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurer.java

@ -231,6 +231,10 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends @@ -231,6 +231,10 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
public void init(H http) throws Exception {
super.init(http);
initDefaultLoginFilter(http);
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
exceptions.defaultAuthenticationEntryPointFor(getAuthenticationEntryPoint(), "FACTOR_PASSWORD");
}
}
@Override

10
config/src/main/java/org/springframework/security/config/annotation/web/configurers/WebAuthnConfigurer.java

@ -28,6 +28,7 @@ import org.springframework.security.authentication.ProviderManager; @@ -28,6 +28,7 @@ import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@ -150,6 +151,15 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>> @@ -150,6 +151,15 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
@Override
public void init(H http) throws Exception {
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"),
"FACTOR_WEBAUTHN");
}
}
@Override
public void configure(H http) throws Exception {
UserDetailsService userDetailsService = getSharedOrBean(http, UserDetailsService.class)

3
config/src/main/java/org/springframework/security/config/annotation/web/configurers/X509Configurer.java

@ -184,7 +184,8 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>> @@ -184,7 +184,8 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
exceptions.defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), AnyRequestMatcher.INSTANCE);
exceptions.defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), AnyRequestMatcher.INSTANCE,
"FACTOR_X509");
}
}

5
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

@ -40,6 +40,7 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder; @@ -40,6 +40,7 @@ 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.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.core.Authentication;
@ -372,6 +373,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> @@ -372,6 +373,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
http.authenticationProvider(new OidcAuthenticationRequestChecker());
}
this.initDefaultLoginFilter(http);
ExceptionHandlingConfigurer<B> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
exceptions.defaultAuthenticationEntryPointFor(getAuthenticationEntryPoint(), "FACTOR_AUTHORIZATION_CODE");
}
}
@Override

4
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java

@ -259,6 +259,10 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder< @@ -259,6 +259,10 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
if (authenticationProvider != null) {
http.authenticationProvider(authenticationProvider);
}
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
exceptions.defaultAuthenticationEntryPointFor(this.authenticationEntryPoint, "FACTOR_BEARER");
}
}
@Override

67
config/src/main/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurer.java

@ -16,10 +16,15 @@ @@ -16,10 +16,15 @@
package org.springframework.security.config.annotation.web.configurers.ott;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
@ -35,8 +40,15 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -35,8 +40,15 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.FormPostRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@ -55,6 +67,7 @@ import org.springframework.security.web.csrf.CsrfToken; @@ -55,6 +67,7 @@ import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
/**
* An {@link AbstractHttpConfigurer} for One-Time Token Login.
@ -134,6 +147,12 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>> @@ -134,6 +147,12 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
AuthenticationProvider authenticationProvider = getAuthenticationProvider();
http.authenticationProvider(postProcess(authenticationProvider));
intiDefaultLoginFilter(http);
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
AuthenticationEntryPoint entryPoint = new PostAuthenticationEntryPoint(
this.tokenGeneratingUrl + "?username={u}", Map.of("u", Authentication::getName));
exceptions.defaultAuthenticationEntryPointFor(entryPoint, "FACTOR_OTT");
}
}
private void intiDefaultLoginFilter(H http) {
@ -391,4 +410,52 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>> @@ -391,4 +410,52 @@ public final class OneTimeTokenLoginConfigurer<H extends HttpSecurityBuilder<H>>
return this.context;
}
private static final class PostAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final String entryPointUri;
private final Map<String, Function<Authentication, String>> params;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
private RedirectStrategy redirectStrategy = new FormPostRedirectStrategy();
private PostAuthenticationEntryPoint(String entryPointUri,
Map<String, Function<Authentication, String>> params) {
this.entryPointUri = entryPointUri;
this.params = params;
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
Authentication authentication = getAuthentication(authException);
Assert.notNull(authentication, "could not find authentication in order to perform post");
Map<String, String> params = this.params.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, (entry) -> entry.getValue().apply(authentication)));
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(this.entryPointUri);
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
builder.queryParam(csrf.getParameterName(), csrf.getToken());
}
String entryPointUrl = builder.build(false).expand(params).toUriString();
this.redirectStrategy.sendRedirect(request, response, entryPointUrl);
}
private Authentication getAuthentication(AuthenticationException authException) {
Authentication authentication = authException.getAuthenticationRequest();
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
}
authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
return authentication;
}
return null;
}
}
}

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

@ -33,6 +33,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -33,6 +33,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider;
@ -304,6 +305,10 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>> @@ -304,6 +305,10 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
if (this.authenticationManager == null) {
registerDefaultAuthenticationProvider(http);
}
ExceptionHandlingConfigurer<B> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
exceptions.defaultAuthenticationEntryPointFor(getAuthenticationEntryPoint(), "FACTOR_SAML_RESPONSE");
}
}
/**

3
web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java

@ -196,7 +196,8 @@ public class ExceptionTranslationFilter extends GenericFilterBean implements Mes @@ -196,7 +196,8 @@ public class ExceptionTranslationFilter extends GenericFilterBean implements Mes
}
AuthenticationException ex = new InsufficientAuthenticationException(
this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication",
"Full authentication is required to access this resource"));
"Full authentication is required to access this resource"),
exception);
ex.setAuthenticationRequest(authentication);
sendStartAuthentication(request, response, chain, ex);
}

Loading…
Cancel
Save