diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java index c27a779ea39..271175ad2e5 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java @@ -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.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.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 @@ * authentication failures are handled *
  • {@link #bearerTokenResolver(BearerTokenResolver)} - customizes how to resolve a * bearer token from the request
  • + *
  • {@link #bearerTokenAuthenticationConverter(AuthenticationConverter)}
  • - + * customizes how to convert a bear token authentication from the request *
  • {@link #jwt(Customizer)} - enables Jwt-encoded bearer token support
  • *
  • {@link #opaqueToken(Customizer)} - enables opaque bearer token support
  • * @@ -159,6 +163,8 @@ public final class OAuth2ResourceServerConfigurer bearerTokenResolver(BearerTokenResolver return this; } + public OAuth2ResourceServerConfigurer 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 void configure(H http) { 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 @@ BearerTokenResolver getBearerTokenResolver() { 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; diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index e7c7de2cd85..5072e6d768e 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -33,6 +33,7 @@ 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.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 void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() 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 @@ BearerTokenResolver resolverTwo() { } + @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 { diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverter.java new file mode 100644 index 00000000000..a10ffd1c3db --- /dev/null +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverter.java @@ -0,0 +1,81 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.resource.authentication; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.security.authentication.AuthenticationDetailsSource; +import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver; +import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver; +import org.springframework.security.web.authentication.AuthenticationConverter; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.Assert; + +/** + * Converts from a HttpServletRequest to {@link BearerTokenAuthenticationToken} that can + * be authenticated. Null authentication possible if there was no Authorization header + * with Bearer Token. + * + * @author Jeongjin Kim + * @since 5.5 + */ +public final class BearerTokenAuthenticationConverter implements AuthenticationConverter { + + private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); + + private BearerTokenResolver bearerTokenResolver; + + public BearerTokenAuthenticationConverter() { + this.bearerTokenResolver = new DefaultBearerTokenResolver(); + } + + @Override + public BearerTokenAuthenticationToken convert(HttpServletRequest request) { + String token = this.bearerTokenResolver.resolve(request); + + if (token == null) { + return null; + } + + BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token); + authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + return authenticationRequest; + } + + /** + * Set the {@link BearerTokenResolver} to use. Defaults to + * {@link DefaultBearerTokenResolver}. + * @param bearerTokenResolver the {@code BearerTokenResolver} to use + */ + public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) { + Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null"); + this.bearerTokenResolver = bearerTokenResolver; + } + + /** + * Set the {@link AuthenticationDetailsSource} to use. Defaults to + * {@link WebAuthenticationDetailsSource}. + * @param authenticationDetailsSource the {@code AuthenticationDetailsSource} to use + */ + public void setAuthenticationDetailsSource( + AuthenticationDetailsSource authenticationDetailsSource) { + Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); + this.authenticationDetailsSource = authenticationDetailsSource; + } + +} diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java index 551101d0ef3..a945cc479a1 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilter.java @@ -24,7 +24,6 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.core.log.LogMessage; -import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManagerResolver; import org.springframework.security.authentication.AuthenticationServiceException; @@ -32,12 +31,12 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider; import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.util.Assert; import org.springframework.web.filter.OncePerRequestFilter; @@ -61,10 +60,6 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter private final AuthenticationManagerResolver authenticationManagerResolver; - private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); - - private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver(); - private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint(); private AuthenticationFailureHandler authenticationFailureHandler = (request, response, exception) -> { @@ -74,6 +69,8 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter this.authenticationEntryPoint.commence(request, response, exception); }; + private AuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter(); + /** * Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s) * @param authenticationManagerResolver @@ -106,22 +103,21 @@ public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManag @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - String token; + Authentication authenticationRequest; try { - token = this.bearerTokenResolver.resolve(request); + authenticationRequest = this.authenticationConverter.convert(request); } - catch (OAuth2AuthenticationException invalid) { + catch (AuthenticationException invalid) { this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid); this.authenticationEntryPoint.commence(request, response, invalid); return; } - if (token == null) { + if (authenticationRequest == null) { this.logger.trace("Did not process request since did not find bearer token"); filterChain.doFilter(request, response); return; } - BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token); - authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + try { AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request); Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest); @@ -144,10 +140,17 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse * Set the {@link BearerTokenResolver} to use. Defaults to * {@link DefaultBearerTokenResolver}. * @param bearerTokenResolver the {@code BearerTokenResolver} to use + * @deprecated Instead, use {@link BearerTokenAuthenticationConverter} explicitly + * @see BearerTokenAuthenticationConverter */ + @Deprecated public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) { Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null"); - this.bearerTokenResolver = bearerTokenResolver; + Assert.isTrue(this.authenticationConverter instanceof BearerTokenAuthenticationConverter, + "bearerTokenResolver and authenticationConverter cannot both be customized in this filter. " + + "Since you've customized the authenticationConverter, " + + "please consider configuring the bearerTokenResolver there."); + ((BearerTokenAuthenticationConverter) this.authenticationConverter).setBearerTokenResolver(bearerTokenResolver); } /** @@ -171,4 +174,15 @@ public void setAuthenticationFailureHandler(final AuthenticationFailureHandler a this.authenticationFailureHandler = authenticationFailureHandler; } + /** + * Set the {@link AuthenticationConverter} to use. Defaults to + * {@link BearerTokenAuthenticationConverter}. + * @param authenticationConverter the {@code AuthenticationConverter} to use + * @since 5.5 + */ + public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { + Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); + this.authenticationConverter = authenticationConverter; + } + } diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverterTests.java new file mode 100644 index 00000000000..8f46a381dd6 --- /dev/null +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationConverterTests.java @@ -0,0 +1,87 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.server.resource.authentication; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.http.HttpHeaders; +import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link BearerTokenAuthenticationConverter} + * + * @author Jeongjin Kim + * @since 5.5 + */ +@RunWith(MockitoJUnitRunner.class) +public class BearerTokenAuthenticationConverterTests { + + private BearerTokenAuthenticationConverter converter; + + @Before + public void setup() { + this.converter = new BearerTokenAuthenticationConverter(); + } + + @Test + public void setBearerTokenResolverWithNullThenThrowsException() { + // @formatter:off + assertThatIllegalArgumentException() + .isThrownBy(() -> this.converter.setBearerTokenResolver(null)) + .withMessageContaining("bearerTokenResolver cannot be null"); + // @formatter:on + } + + @Test + public void setAuthenticationDetailsSourceWithNullThenThrowsException() { + // @formatter:off + assertThatIllegalArgumentException() + .isThrownBy(() -> this.converter.setAuthenticationDetailsSource(null)) + .withMessageContaining("authenticationDetailsSource cannot be null"); + // @formatter:on + } + + @Test + public void convertWhenNoBearerTokenHeaderThenNull() { + HttpServletRequest request = mock(HttpServletRequest.class); + + BearerTokenAuthenticationToken convert = this.converter.convert(request); + + assertThat(convert).isNull(); + } + + @Test + public void convertWhenBearerTokenThenBearerTokenAuthenticationToken() { + HttpServletRequest request = mock(HttpServletRequest.class); + given(request.getHeader(HttpHeaders.AUTHORIZATION)).willReturn("Bearer token"); + + BearerTokenAuthenticationToken token = this.converter.convert(request); + + assertThat(token.getToken()).isEqualTo("token"); + } + +} diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java index 3882f73f72c..af4b3503703 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationFilterTests.java @@ -187,6 +187,16 @@ public void setBearerTokenResolverWhenNullThenThrowsException() { // @formatter:on } + @Test + public void setAuthenticationConverterWhenNullThenThrowsException() { + // @formatter:off + BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager); + assertThatIllegalArgumentException() + .isThrownBy(() -> filter.setAuthenticationConverter(null)) + .withMessageContaining("authenticationConverter cannot be null"); + // @formatter:on + } + @Test public void constructorWhenNullAuthenticationManagerThenThrowsException() { // @formatter:off