1
0
mirror of synced 2026-08-05 17:57:15 +00:00

Refactor AuthenticationDetailsSource support

- BearerTokenAuthenticationFilter exposes this directly, simplifying
configuration and removing a package tangle

Closes gh-9576
This commit is contained in:
Josh Cummings
2021-04-08 17:27:34 -06:00
parent 21f9876d87
commit 7ded671858
6 changed files with 78 additions and 301 deletions
@@ -39,7 +39,6 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
@@ -52,7 +51,6 @@ import org.springframework.security.oauth2.server.resource.web.DefaultBearerToke
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
@@ -80,8 +78,6 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* authentication failures are handled
* <li>{@link #bearerTokenResolver(BearerTokenResolver)} - customizes how to resolve a
* bearer token from the request</li>
* <li>{@link #authenticationConverter(AuthenticationConverter)}</li> - customizes how to
* convert a bearer token authentication from the request
* <li>{@link #jwt(Customizer)} - enables Jwt-encoded bearer token support</li>
* <li>{@link #opaqueToken(Customizer)} - enables opaque bearer token support</li>
* </ul>
@@ -163,8 +159,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private BearerTokenRequestMatcher requestMatcher = new BearerTokenRequestMatcher();
private AuthenticationConverter authenticationConverter;
public OAuth2ResourceServerConfigurer(ApplicationContext context) {
Assert.notNull(context, "context cannot be null");
this.context = context;
@@ -195,12 +189,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
return this;
}
public JwtConfigurer jwt() {
if (this.jwtConfigurer == null) {
this.jwtConfigurer = new JwtConfigurer(this.context);
@@ -265,10 +253,8 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
resolver = (request) -> authenticationManager;
}
this.authenticationConverter = getAuthenticationConverter();
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
filter.setAuthenticationConverter(this.authenticationConverter);
filter.setBearerTokenResolver(bearerTokenResolver);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter = postProcess(filter);
http.addFilter(filter);
@@ -362,20 +348,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this.bearerTokenResolver;
}
AuthenticationConverter getAuthenticationConverter() {
if (this.authenticationConverter == null) {
if (this.context.getBeanNamesForType(BearerTokenAuthenticationConverter.class).length > 0) {
this.authenticationConverter = this.context.getBean(BearerTokenAuthenticationConverter.class);
}
else {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
converter.setBearerTokenResolver(getBearerTokenResolver());
this.authenticationConverter = converter;
}
}
return this.authenticationConverter;
}
public class JwtConfigurer {
private final ApplicationContext context;
@@ -75,11 +75,13 @@ import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -109,20 +111,20 @@ import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.TestJwts;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.test.web.servlet.MockMvc;
@@ -724,68 +726,14 @@ public class OAuth2ResourceServerConfigurerTests {
}
@Test
public void getBearerTokenAuthenticationConverterWhenDuplicateConverterBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {
BearerTokenAuthenticationConverter converterBean = new BearerTokenAuthenticationConverter();
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean("converterOne", BearerTokenAuthenticationConverter.class, () -> converterBean);
context.registerBean("converterTwo", BearerTokenAuthenticationConverter.class, () -> converterBean);
this.spring.context(context).autowire();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
oauth2.authenticationConverter(converter);
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
}
@Test
public void getBearerTokenAuthenticationConverterWhenDuplicateConverterBeansThenWiringException() {
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> this.spring
.register(MultipleBearerTokenAuthenticationConverterBeansConfig.class, JwtDecoderConfig.class)
.autowire()).withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);
}
@Test
public void getBearerTokenAuthenticationConverterWhenConverterBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
BearerTokenAuthenticationConverter converterBean = new BearerTokenAuthenticationConverter();
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean(BearerTokenAuthenticationConverter.class, () -> converterBean);
this.spring.context(context).autowire();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
oauth2.authenticationConverter(converter);
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
}
@Test
public void getBearerTokenAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() {
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
assertThat(oauth2.getAuthenticationConverter()).isInstanceOf(BearerTokenAuthenticationConverter.class);
}
@Test
public void getBearerTokenAuthenticationConverterWhenConverterBeanRegisteredThenBeanIsUsed() {
BearerTokenAuthenticationConverter converterBean = new BearerTokenAuthenticationConverter();
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean(BearerTokenAuthenticationConverter.class, () -> converterBean);
this.spring.context(context).autowire();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converterBean);
}
@Test
public void getBearerTokenAuthenticationConverterWhenOnlyResolverBeanRegisteredThenUseTheResolver() {
HttpServletRequest servletRequest = mock(HttpServletRequest.class);
BearerTokenResolver resolverBean = (request) -> "bearer customToken";
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean(BearerTokenResolver.class, () -> resolverBean);
this.spring.context(context).autowire();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
BearerTokenAuthenticationToken bearerTokenAuthenticationToken = (BearerTokenAuthenticationToken) oauth2
.getAuthenticationConverter().convert(servletRequest);
String token = bearerTokenAuthenticationToken.getToken();
assertThat(token).isEqualTo("bearer customToken");
public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
.autowire();
JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
given(decoder.decode(anyString())).willReturn(JWT);
this.mvc.perform(get("/authenticated").with(bearerToken(JWT_TOKEN))).andExpect(status().isOk())
.andExpect(content().string(JWT_SUBJECT));
verifyBean(AuthenticationDetailsSource.class).buildDetails(any());
}
@Test
@@ -1940,29 +1888,35 @@ public class OAuth2ResourceServerConfigurerTests {
}
@EnableWebSecurity
static class MultipleBearerTokenAuthenticationConverterBeansConfig extends WebSecurityConfigurerAdapter {
static class CustomAuthenticationDetailsSource {
@Override
protected void configure(HttpSecurity http) throws Exception {
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = mock(
AuthenticationDetailsSource.class);
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
// @formatter:off
http
.oauth2ResourceServer()
.jwt();
// @formatter:on
.authorizeRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(withDefaults())
.withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {
@Override
public BearerTokenAuthenticationFilter postProcess(BearerTokenAuthenticationFilter object) {
object.setAuthenticationDetailsSource(CustomAuthenticationDetailsSource.this.authenticationDetailsSource);
return object;
}
})
);
return http.build();
}
@Bean
BearerTokenAuthenticationConverter converterOne() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
return converter;
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource() {
return this.authenticationDetailsSource;
}
@Bean
BearerTokenAuthenticationConverter converterTwo() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
return converter;
}
}
@EnableWebSecurity