Set details on authentication token created by HttpServlet3RequestFactory

Currently the login mechanism when triggered by executing HttpServlet3RequestFactory#login does not set any details on the underlying authentication token that is authenticated.

This change adds an AuthenticationDetailsSource on the HttpServlet3RequestFactory, which defaults to a WebAuthenticationDetailsSource.

Closes gh-9579
This commit is contained in:
Karl Tinawi 2021-04-11 15:13:13 +01:00 committed by Steve Riesenberg
parent be802f57ba
commit c57fc309c2
4 changed files with 71 additions and 1 deletions

View File

@ -21,6 +21,7 @@ import java.util.List;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@ -90,6 +91,11 @@ public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>>
if (trustResolver != null) {
this.securityContextRequestFilter.setTrustResolver(trustResolver);
}
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = http
.getSharedObject(AuthenticationDetailsSource.class);
if (authenticationDetailsSource != null) {
this.securityContextRequestFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (context != null) {
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);

View File

@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.TestingAuthenticationToken;
@ -149,6 +150,15 @@ public class ServletApiConfigurerTests {
verify(SharedTrustResolverConfig.TR, atLeastOnce()).isAnonymous(any());
}
@Test
public void configureWhenSharedObjectAuthenticationDetailsSourceThenAuthenticationDetailsSourceUsed() {
this.spring.register(SharedAuthenticationDetailsSourceConfig.class).autowire();
SecurityContextHolderAwareRequestFilter scaFilter = getFilter(SecurityContextHolderAwareRequestFilter.class);
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = getFieldValue(scaFilter,
"authenticationDetailsSource");
assertThat(authenticationDetailsSource).isEqualTo(SharedAuthenticationDetailsSourceConfig.ADS);
}
@Test
public void requestWhenServletApiWithDefaultsInLambdaThenUsesDefaultRolePrefix() throws Exception {
this.spring.register(ServletApiWithDefaultsInLambdaConfig.class, AdminController.class).autowire();
@ -321,6 +331,22 @@ public class ServletApiConfigurerTests {
}
@EnableWebSecurity
static class SharedAuthenticationDetailsSourceConfig extends WebSecurityConfigurerAdapter {
@SuppressWarnings("unchecked")
static AuthenticationDetailsSource<HttpServletRequest, ?> ADS = spy(AuthenticationDetailsSource.class);
@Override
protected void configure(HttpSecurity http) {
// @formatter:off
http
.setSharedObject(AuthenticationDetailsSource.class, ADS);
// @formatter:on
}
}
@EnableWebSecurity
static class ServletApiWithDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {

View File

@ -32,6 +32,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
@ -42,6 +43,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@ -79,6 +81,8 @@ final class HttpServlet3RequestFactory implements HttpServletRequestFactory {
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationManager authenticationManager;
@ -158,6 +162,18 @@ final class HttpServlet3RequestFactory implements HttpServletRequestFactory {
this.trustResolver = trustResolver;
}
/**
* Sets the {@link AuthenticationDetailsSource} to be used. The default is
* {@link WebAuthenticationDetailsSource}.
* @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use.
* Cannot be null.
*/
void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
this.authenticationDetailsSource = authenticationDetailsSource;
}
@Override
public HttpServletRequest create(HttpServletRequest request, HttpServletResponse response) {
return new Servlet3SecurityContextHolderAwareRequestWrapper(request, this.rolePrefix, response);
@ -233,7 +249,11 @@ final class HttpServlet3RequestFactory implements HttpServletRequestFactory {
private Authentication getAuthentication(AuthenticationManager authManager, String username, String password)
throws ServletException {
try {
return authManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username,
password);
Object details = HttpServlet3RequestFactory.this.authenticationDetailsSource.buildDetails(this);
authentication.setDetails(details);
return authManager.authenticate(authentication);
}
catch (AuthenticationException ex) {
SecurityContextHolder.clearContext();

View File

@ -27,12 +27,14 @@ import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
@ -80,6 +82,8 @@ public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean {
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
public void setRolePrefix(String rolePrefix) {
Assert.notNull(rolePrefix, "Role prefix must not be null");
this.rolePrefix = rolePrefix;
@ -172,9 +176,23 @@ public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean {
updateFactory();
}
/**
* Sets the {@link AuthenticationDetailsSource} to be used. The default is
* {@link WebAuthenticationDetailsSource}.
* @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use.
* Cannot be null.
*/
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
this.authenticationDetailsSource = authenticationDetailsSource;
updateFactory();
}
private HttpServletRequestFactory createServlet3Factory(String rolePrefix) {
HttpServlet3RequestFactory factory = new HttpServlet3RequestFactory(rolePrefix);
factory.setTrustResolver(this.trustResolver);
factory.setAuthenticationDetailsSource(this.authenticationDetailsSource);
factory.setAuthenticationEntryPoint(this.authenticationEntryPoint);
factory.setAuthenticationManager(this.authenticationManager);
factory.setLogoutHandlers(this.logoutHandlers);