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

Add BearerTokenAuthenticationConverter

BearerTokenAuthenticationConverter is introduced to solve the
problem of not being able to change AuthenticationDetailsSource.
BearerTokenAuthenticationFilter delegates to
BearerTokenAuthenticationConverter the task of creating
BearerTokenAuthenticationToken and setting AuthenticationDetailsSource.
BearerTokenAuthenticationConverter is customizable and the customized
converter can be used in BearerTokenAuthenticationFilter.

Closes gh-8840
This commit is contained in:
Jeongjin Kim
2020-08-19 16:40:04 +09:00
committed by Josh Cummings
parent efb394d3b2
commit 31f310fd22
6 changed files with 333 additions and 16 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ 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;
@@ -51,6 +52,7 @@ 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;
@@ -78,6 +80,8 @@ 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 #bearerTokenAuthenticationConverter(AuthenticationConverter)}</li> -
* customizes how to convert a bear 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>
@@ -159,6 +163,8 @@ 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;
@@ -189,6 +195,13 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
public OAuth2ResourceServerConfigurer<H> bearerTokenAuthenticationConverter(
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);
@@ -252,8 +265,11 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
AuthenticationManager authenticationManager = getAuthenticationManager(http);
resolver = (request) -> authenticationManager;
}
this.authenticationConverter = getBearerTokenAuthenticationConverter();
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
filter.setBearerTokenResolver(bearerTokenResolver);
filter.setAuthenticationConverter(this.authenticationConverter);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter = postProcess(filter);
http.addFilter(filter);
@@ -347,6 +363,20 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this.bearerTokenResolver;
}
AuthenticationConverter getBearerTokenAuthenticationConverter() {
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;
@@ -33,6 +33,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
@@ -108,7 +109,9 @@ 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;
@@ -720,6 +723,72 @@ public class OAuth2ResourceServerConfigurerTests {
assertThat(oauth2.getBearerTokenResolver()).isInstanceOf(DefaultBearerTokenResolver.class);
}
@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.bearerTokenAuthenticationConverter(converter);
assertThat(oauth2.getBearerTokenAuthenticationConverter()).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.bearerTokenAuthenticationConverter(converter);
assertThat(oauth2.getBearerTokenAuthenticationConverter()).isEqualTo(converter);
}
@Test
public void getBearerTokenAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() {
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
assertThat(oauth2.getBearerTokenAuthenticationConverter())
.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.getBearerTokenAuthenticationConverter()).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
.getBearerTokenAuthenticationConverter().convert(servletRequest);
String token = bearerTokenAuthenticationToken.getToken();
assertThat(token).isEqualTo("bearer customToken");
}
@Test
public void requestWhenCustomJwtDecoderWiredOnDslThenUsed() throws Exception {
this.spring.register(CustomJwtDecoderOnDsl.class, BasicController.class).autowire();
@@ -1871,6 +1940,32 @@ public class OAuth2ResourceServerConfigurerTests {
}
@EnableWebSecurity
static class MultipleBearerTokenAuthenticationConverterBeansConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.oauth2ResourceServer()
.jwt();
// @formatter:on
}
@Bean
BearerTokenAuthenticationConverter converterOne() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
return converter;
}
@Bean
BearerTokenAuthenticationConverter converterTwo() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
return converter;
}
}
@EnableWebSecurity
static class CustomJwtDecoderOnDsl extends WebSecurityConfigurerAdapter {