Add Support BearerTokenAuthenticationConverter

Closes gh-14750

Signed-off-by: Max Batischev <mblancer@mail.ru>
This commit is contained in:
Max Batischev 2025-04-11 15:26:00 +03:00 committed by Josh Cummings
parent 3f0326d3f1
commit 4967f3feee
15 changed files with 722 additions and 58 deletions

View File

@ -23,6 +23,8 @@ import java.util.Map;
import java.util.function.Supplier;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.converter.Converter;
@ -37,10 +39,12 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
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.BearerTokenAuthenticationToken;
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;
@ -49,13 +53,14 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
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.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.csrf.CsrfException;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@ -64,6 +69,7 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
@ -156,7 +162,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private BearerTokenResolver bearerTokenResolver;
private AuthenticationConverter authenticationConverter;
private JwtConfigurer jwtConfigurer;
@ -194,9 +200,25 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
/**
* @deprecated please use {@link #authenticationConverter} instead
*/
@Deprecated
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
this.bearerTokenResolver = bearerTokenResolver;
this.authenticationConverter = new BearerTokenResolverAuthenticationConverterAdapter(bearerTokenResolver);
return this;
}
/**
* Sets the {@link AuthenticationConverter} to use.
* @param authenticationConverter the authentication converter
* @return the {@link OAuth2ResourceServerConfigurer} for further configuration
* @since 6.5
*/
public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
return this;
}
@ -271,8 +293,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
@Override
public void configure(H http) {
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
if (resolver == null) {
AuthenticationManager authenticationManager = getAuthenticationManager(http);
@ -280,7 +300,9 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
}
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
filter.setBearerTokenResolver(bearerTokenResolver);
AuthenticationConverter converter = getAuthenticationConverter();
this.requestMatcher.setAuthenticationConverter(converter);
filter.setAuthenticationConverter(converter);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
filter = postProcess(filter);
@ -363,16 +385,29 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return http.getSharedObject(AuthenticationManager.class);
}
BearerTokenResolver getBearerTokenResolver() {
if (this.bearerTokenResolver == null) {
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
this.bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
}
else {
this.bearerTokenResolver = new DefaultBearerTokenResolver();
}
AuthenticationConverter getAuthenticationConverter() {
if (this.authenticationConverter != null) {
return this.authenticationConverter;
}
return this.bearerTokenResolver;
if (this.context.getBeanNamesForType(AuthenticationConverter.class).length > 0) {
this.authenticationConverter = this.context.getBean(AuthenticationConverter.class);
}
else if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
BearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
this.authenticationConverter = new BearerTokenResolverAuthenticationConverterAdapter(bearerTokenResolver);
}
else {
this.authenticationConverter = new BearerTokenAuthenticationConverter();
}
return this.authenticationConverter;
}
BearerTokenResolver getBearerTokenResolver() {
AuthenticationConverter authenticationConverter = getAuthenticationConverter();
if (authenticationConverter instanceof BearerTokenResolverAuthenticationConverterAdapter bearer) {
return bearer.bearerTokenResolver;
}
return null;
}
public class JwtConfigurer {
@ -560,21 +595,43 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private static final class BearerTokenRequestMatcher implements RequestMatcher {
private BearerTokenResolver bearerTokenResolver;
private AuthenticationConverter authenticationConverter;
@Override
public boolean matches(HttpServletRequest request) {
try {
return this.bearerTokenResolver.resolve(request) != null;
return this.authenticationConverter.convert(request) != null;
}
catch (OAuth2AuthenticationException ex) {
return false;
}
}
void setBearerTokenResolver(BearerTokenResolver tokenResolver) {
Assert.notNull(tokenResolver, "resolver cannot be null");
this.bearerTokenResolver = tokenResolver;
void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
}
}
private static final class BearerTokenResolverAuthenticationConverterAdapter implements AuthenticationConverter {
private final Log logger = LogFactory.getLog(BearerTokenResolverAuthenticationConverterAdapter.class);
private final BearerTokenResolver bearerTokenResolver;
BearerTokenResolverAuthenticationConverterAdapter(BearerTokenResolver bearerTokenResolver) {
this.bearerTokenResolver = bearerTokenResolver;
}
@Override
public Authentication convert(HttpServletRequest request) {
String token = this.bearerTokenResolver.resolve(request);
if (!StringUtils.hasText(token)) {
this.logger.trace("Did not process request since did not find bearer token");
return null;
}
return new BearerTokenAuthenticationToken(token);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
@ -43,9 +44,10 @@ import org.springframework.security.oauth2.server.resource.authentication.Opaque
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
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.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@ -64,10 +66,14 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
static final String AUTHENTICATION_CONVERTER_REF = "authentication-converter-ref";
static final String ENTRY_POINT_REF = "entry-point-ref";
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
static final String AUTHENTICATION_CONVERTER = "authenticationConverter";
static final String AUTHENTICATION_ENTRY_POINT = "authenticationEntryPoint";
private final BeanReference authenticationManager;
@ -124,11 +130,16 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
}
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
BeanMetadataElement authenticationConverter = getAuthenticationConverter(oauth2ResourceServer);
if (bearerTokenResolver != null && authenticationConverter != null) {
throw new BeanDefinitionStoreException(
"You cannot use bearer-token-ref and authentication-converter-ref in the same oauth2-resource-server element");
}
if (bearerTokenResolver == null && authenticationConverter == null) {
authenticationConverter = new RootBeanDefinition(BearerTokenAuthenticationConverter.class);
}
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
BeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
this.ignoreCsrfRequestMatchers.add(requestMatcher);
@ -136,13 +147,33 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
filterBuilder.addPropertyValue("securityContextHolderStrategy",
this.authenticationFilterSecurityContextHolderStrategy);
if (authenticationConverter != null) {
filterBuilder.addPropertyValue(AUTHENTICATION_CONVERTER, authenticationConverter);
}
if (bearerTokenResolver != null) {
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
}
return filterBuilder.getBeanDefinition();
}
private BeanDefinition buildRequestMatcher(BeanMetadataElement bearerTokenResolver,
BeanMetadataElement authenticationConverter) {
if (bearerTokenResolver != null) {
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
return requestMatcherBuilder.getBeanDefinition();
}
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(authenticationConverter);
return requestMatcherBuilder.getBeanDefinition();
}
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
if (jwt == null && opaqueToken == null) {
@ -178,11 +209,19 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
BeanMetadataElement getBearerTokenResolver(Element element) {
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
if (!StringUtils.hasLength(bearerTokenResolverRef)) {
return new RootBeanDefinition(DefaultBearerTokenResolver.class);
return null;
}
return new RuntimeBeanReference(bearerTokenResolverRef);
}
BeanMetadataElement getAuthenticationConverter(Element element) {
String authenticationConverterRef = element.getAttribute(AUTHENTICATION_CONVERTER_REF);
if (!StringUtils.hasLength(authenticationConverterRef)) {
return null;
}
return new RuntimeBeanReference(authenticationConverterRef);
}
BeanMetadataElement getEntryPoint(Element element) {
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
if (!StringUtils.hasLength(entryPointRef)) {
@ -366,4 +405,25 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
}
static final class BearerTokenAuthenticationRequestMatcher implements RequestMatcher {
private final AuthenticationConverter authenticationConverter;
BearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
}
@Override
public boolean matches(HttpServletRequest request) {
try {
return this.authenticationConverter.convert(request) != null;
}
catch (OAuth2AuthenticationException ex) {
return false;
}
}
}
}

View File

@ -650,6 +650,9 @@ oauth2-resource-server.attlist &=
oauth2-resource-server.attlist &=
## Reference to a AuthenticationEntryPoint
attribute entry-point-ref {xsd:token}?
oauth2-resource-server.attlist &=
## Reference to a AuthenticationConverter
attribute authentication-converter-ref {xsd:token}?
jwt =
## Configures JWT authentication

View File

@ -1999,6 +1999,12 @@
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="authentication-converter-ref" type="xs:token">
<xs:annotation>
<xs:documentation>Reference to a AuthenticationConverter
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="jwt">
<xs:annotation>

View File

@ -127,12 +127,14 @@ import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthen
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.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
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.security.web.authentication.AuthenticationConverter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultMatcher;
@ -759,13 +761,6 @@ public class OAuth2ResourceServerConfigurerTests {
assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
}
@Test
public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() {
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
assertThat(oauth2.getBearerTokenResolver()).isInstanceOf(DefaultBearerTokenResolver.class);
}
@Test
public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
@ -1415,6 +1410,47 @@ public class OAuth2ResourceServerConfigurerTests {
verify(authenticationConverter).convert(any(), any());
}
@Test
public void getAuthenticationConverterWhenDuplicateConverterBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {
AuthenticationConverter converter = mock(AuthenticationConverter.class);
AuthenticationConverter converterBean = mock(AuthenticationConverter.class);
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean("converterOne", AuthenticationConverter.class, () -> converterBean);
context.registerBean("converterTwo", AuthenticationConverter.class, () -> converterBean);
this.spring.context(context).autowire();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
oauth2.authenticationConverter(converter);
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
}
@Test
public void getAuthenticationConverterWhenConverterBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {
AuthenticationConverter converter = mock(AuthenticationConverter.class);
AuthenticationConverter converterBean = mock(AuthenticationConverter.class);
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean(AuthenticationConverter.class, () -> converterBean);
this.spring.context(context).autowire();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
oauth2.authenticationConverter(converter);
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
}
@Test
public void getAuthenticationConverterWhenDuplicateConverterBeansThenWiringException() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(
() -> this.spring.register(MultipleAuthenticationConverterBeansConfig.class, JwtDecoderConfig.class)
.autowire())
.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);
}
@Test
public void getAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() {
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
assertThat(oauth2.getAuthenticationConverter()).isInstanceOf(BearerTokenAuthenticationConverter.class);
}
private static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {
context.registerBean(name, clazz, () -> mock(clazz));
}
@ -2516,6 +2552,39 @@ public class OAuth2ResourceServerConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class MultipleAuthenticationConverterBeansConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
// @formatter:on
}
@Bean
AuthenticationConverter authenticationConverterOne() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
converter.setAllowUriQueryParameter(true);
return converter;
}
@Bean
AuthenticationConverter authenticationConverterTwo() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
converter.setAllowUriQueryParameter(true);
return converter;
}
}
@Configuration
@EnableWebSecurity
static class MultipleIssuersConfig {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 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.
@ -25,7 +25,6 @@ import java.time.Instant;
import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
@ -50,13 +49,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.XmlReaderContext;
@ -85,12 +82,14 @@ import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.TestJwts;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
@ -462,6 +461,24 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
verify(bearerTokenResolver).resolve(any(HttpServletRequest.class));
}
@Test
public void getWhenCustomAuthenticationConverterThenUses() throws Exception {
this.spring
.configLocations(xml("MockAuthenticationConverter"), xml("MockJwtDecoder"), xml("AuthenticationConverter"))
.autowire();
JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
given(decoder.decode("token")).willReturn(TestJwts.jwt().build());
AuthenticationConverter authenticationConverter = this.spring.getContext()
.getBean(AuthenticationConverter.class);
given(authenticationConverter.convert(any(HttpServletRequest.class)))
.willReturn(new BearerTokenAuthenticationToken("token"));
this.mvc.perform(get("/")).andExpect(status().isNotFound());
verify(decoder).decode("token");
verify(authenticationConverter).convert(any(HttpServletRequest.class));
}
@Test
public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
throws Exception {
@ -521,14 +538,6 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
// @formatter:on
}
@Test
public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() {
OAuth2ResourceServerBeanDefinitionParser oauth2 = new OAuth2ResourceServerBeanDefinitionParser(
mock(BeanReference.class), mock(List.class), mock(Map.class), mock(Map.class), mock(List.class),
mock(BeanMetadataElement.class));
assertThat(oauth2.getBearerTokenResolver(mock(Element.class))).isInstanceOf(RootBeanDefinition.class);
}
@Test
public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
@ -545,6 +554,12 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
}
@Test
public void configureWhenAuthenticationConverterAndJwkSetUriThenException() {
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(
() -> this.spring.configLocations(xml("AuthenticationConverterAndBearerTokenResolver")).autowire());
}
@Test
public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2020 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.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<http>
<intercept-url pattern="/**" access="authenticated"/>
<oauth2-resource-server authentication-converter-ref="authenticationConverter">
<jwt decoder-ref="decoder"/>
</oauth2-resource-server>
</http>
<b:import resource="userservice.xml"/>
</b:beans>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2020 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.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<http>
<intercept-url pattern="/**" access="authenticated"/>
<oauth2-resource-server authentication-converter-ref="authenticationConverter" bearer-token-resolver-ref="bearerTokenResolver">
<jwt decoder-ref="decoder"/>
</oauth2-resource-server>
</http>
<b:import resource="userservice.xml"/>
</b:beans>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2020 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.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<b:bean name="authenticationConverter" class="org.mockito.Mockito" factory-method="mock">
<b:constructor-arg value="org.springframework.security.web.authentication.AuthenticationConverter" type="java.lang.Class"/>
</b:bean>
</b:beans>

View File

@ -1272,6 +1272,10 @@ Reference to a `BearerTokenResolver` which will retrieve the bearer token from t
* **entry-point-ref**
Reference to a `AuthenticationEntryPoint` which will handle unauthorized requests
[[nsa-oauth2-resource-server-authentication-converter-ref]]
* **authentication-converter-ref**
Reference to a `AuthenticationConverter` which convert request to authentication
[[nsa-jwt]]
== <jwt>
Represents an OAuth 2.0 Resource Server that will authorize JWTs

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2025 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.
@ -29,7 +29,10 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
* @since 5.1
* @see <a href="https://tools.ietf.org/html/rfc6750#section-2" target="_blank">RFC 6750
* Section 2: Authenticated Requests</a>
* @deprecated Use
* {@link org.springframework.security.web.authentication.AuthenticationConverter} instead
*/
@Deprecated
@FunctionalInterface
public interface BearerTokenResolver {

View File

@ -0,0 +1,181 @@
/*
* Copyright 2002-2025 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.web.authentication;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.oauth2.server.resource.BearerTokenErrors;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Implementation of {@link AuthenticationConverter}, that converts request to
* {@link BearerTokenAuthenticationToken}
*
* @author Max Batischev
* @since 6.5
*/
public final class BearerTokenAuthenticationConverter implements AuthenticationConverter {
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$",
Pattern.CASE_INSENSITIVE);
private static final String ACCESS_TOKEN_PARAMETER_NAME = "access_token";
private boolean allowFormEncodedBodyParameter = false;
private boolean allowUriQueryParameter = false;
private String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION;
@Override
public Authentication convert(HttpServletRequest request) {
String token = resolveToken(request);
if (StringUtils.hasText(token)) {
BearerTokenAuthenticationToken authenticationToken = new BearerTokenAuthenticationToken(token);
authenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));
return authenticationToken;
}
return null;
}
private String resolveToken(HttpServletRequest request) {
final String authorizationHeaderToken = resolveFromAuthorizationHeader(request);
final String parameterToken = isParameterTokenSupportedForRequest(request)
? resolveFromRequestParameters(request) : null;
if (authorizationHeaderToken != null) {
if (parameterToken != null) {
final BearerTokenError error = BearerTokenErrors
.invalidRequest("Found multiple bearer tokens in the request");
throw new OAuth2AuthenticationException(error);
}
return authorizationHeaderToken;
}
if (parameterToken != null && isParameterTokenEnabledForRequest(request)) {
return parameterToken;
}
return null;
}
private String resolveFromAuthorizationHeader(HttpServletRequest request) {
String authorization = request.getHeader(this.bearerTokenHeaderName);
if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
return null;
}
Matcher matcher = authorizationPattern.matcher(authorization);
if (!matcher.matches()) {
BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");
throw new OAuth2AuthenticationException(error);
}
return matcher.group("token");
}
private boolean isParameterTokenEnabledForRequest(HttpServletRequest request) {
return ((this.allowFormEncodedBodyParameter && isFormEncodedRequest(request) && !isGetRequest(request)
&& !hasAccessTokenInQueryString(request)) || (this.allowUriQueryParameter && isGetRequest(request)));
}
private static String resolveFromRequestParameters(HttpServletRequest request) {
String[] values = request.getParameterValues(ACCESS_TOKEN_PARAMETER_NAME);
if (values == null || values.length == 0) {
return null;
}
if (values.length == 1) {
return values[0];
}
BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request");
throw new OAuth2AuthenticationException(error);
}
private boolean isParameterTokenSupportedForRequest(final HttpServletRequest request) {
return isFormEncodedRequest(request) || isGetRequest(request);
}
private boolean isGetRequest(HttpServletRequest request) {
return HttpMethod.GET.name().equals(request.getMethod());
}
private boolean isFormEncodedRequest(HttpServletRequest request) {
return MediaType.APPLICATION_FORM_URLENCODED_VALUE.equals(request.getContentType());
}
private static boolean hasAccessTokenInQueryString(HttpServletRequest request) {
return (request.getQueryString() != null) && request.getQueryString().contains(ACCESS_TOKEN_PARAMETER_NAME);
}
/**
* Set if transport of access token using URI query parameter is supported. Defaults
* to {@code false}.
*
* The spec recommends against using this mechanism for sending bearer tokens, and
* even goes as far as stating that it was only included for completeness.
* @param allowUriQueryParameter if the URI query parameter is supported
*/
public void setAllowUriQueryParameter(boolean allowUriQueryParameter) {
this.allowUriQueryParameter = allowUriQueryParameter;
}
/**
* Set this value to configure what header is checked when resolving a Bearer Token.
* This value is defaulted to {@link HttpHeaders#AUTHORIZATION}.
*
* This allows other headers to be used as the Bearer Token source such as
* {@link HttpHeaders#PROXY_AUTHORIZATION}
* @param bearerTokenHeaderName the header to check when retrieving the Bearer Token.
*/
public void setBearerTokenHeaderName(String bearerTokenHeaderName) {
this.bearerTokenHeaderName = bearerTokenHeaderName;
}
/**
* Set if transport of access token using form-encoded body parameter is supported.
* Defaults to {@code false}.
* @param allowFormEncodedBodyParameter if the form-encoded body parameter is
* supported
*/
public void setAllowFormEncodedBodyParameter(boolean allowFormEncodedBodyParameter) {
this.allowFormEncodedBodyParameter = allowFormEncodedBodyParameter;
}
/**
* Set the {@link AuthenticationDetailsSource} to use. Defaults to
* {@link WebAuthenticationDetailsSource}.
* @param authenticationDetailsSource the {@code AuthenticationDetailsSource} to use
*/
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
this.authenticationDetailsSource = authenticationDetailsSource;
}
}

View File

@ -44,6 +44,7 @@ import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthen
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@ -83,12 +84,12 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
private AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
(request, response, exception) -> this.authenticationEntryPoint.commence(request, response, exception));
private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
private AuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
/**
* Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
* @param authenticationManagerResolver
@ -121,24 +122,22 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
@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) {
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);
@ -194,7 +193,14 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
*/
public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
this.bearerTokenResolver = bearerTokenResolver;
this.authenticationConverter = (request) -> {
String token = bearerTokenResolver.resolve(request);
if (!StringUtils.hasText(token)) {
this.logger.trace("Did not process request since did not find bearer token");
return null;
}
return new BearerTokenAuthenticationToken(token);
};
}
/**
@ -243,4 +249,15 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
return StringUtils.hasText(jwkThumbprintClaim);
}
/**
* Set the {@link AuthenticationConverter} to use. Defaults to
* {@link BearerTokenAuthenticationConverter}.
* @param authenticationConverter the {@code AuthenticationConverter} to use
* @since 6.5
*/
public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter;
}
}

View File

@ -0,0 +1,148 @@
/*
* Copyright 2002-2025 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.web.authentication;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link BearerTokenAuthenticationConverter}
*
* @author Max Batischev
*/
public class BearerTokenAuthenticationConverterTests {
private static final String X_AUTH_TOKEN_HEADER = "X-Auth-Token";
private static final String TEST_X_AUTH_TOKEN = "test-x-auth-token";
private static final String BEARER_TOKEN = "test_bearer_token";
private final BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
@Test
public void convertWhenAuthorizationHeaderIsPresentThenTokenIsConverted() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN);
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNotNull();
}
@Test
public void convertWhenQueryParameterIsPresentThenTokenIsConverted() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod(HttpMethod.GET.name());
request.addParameter("access_token", BEARER_TOKEN);
this.converter.setAllowUriQueryParameter(true);
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNotNull();
}
@Test
public void convertWhenAuthorizationHeaderNotIsPresentThenTokenIsNotConverted() {
MockHttpServletRequest request = new MockHttpServletRequest();
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNull();
}
@Test
public void convertWhenAuthorizationHeaderIsPresentTogetherWithQueryParameterThenAuthenticationExceptionIsThrown() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addParameter("access_token", BEARER_TOKEN);
request.setMethod(HttpMethod.GET.name());
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN);
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))
.withMessageContaining("Found multiple bearer tokens in the request");
}
@Test
public void convertWhenXAuthTokenHeaderIsPresentAndBearerTokenHeaderNameSetThenTokenIsConverted() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(X_AUTH_TOKEN_HEADER, "Bearer " + TEST_X_AUTH_TOKEN);
this.converter.setBearerTokenHeaderName(X_AUTH_TOKEN_HEADER);
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNotNull();
}
@Test
public void convertWhenHeaderWithMissingTokenIsPresentThenAuthenticationExceptionIsThrown() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer ");
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))
.withMessageContaining(("Bearer token is malformed"));
}
@Test
public void convertWhenHeaderWithInvalidCharactersIsPresentThenAuthenticationExceptionIsThrown() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer an\"invalid\"token");
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))
.withMessageContaining(("Bearer token is malformed"));
}
@Test
@SuppressWarnings("unchecked")
public void convertWhenCustomAuthenticationDetailsSourceSetThenTokenIsConverted() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN);
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = Mockito
.mock(AuthenticationDetailsSource.class);
this.converter.setAuthenticationDetailsSource(authenticationDetailsSource);
Authentication authentication = this.converter.convert(request);
verify(authenticationDetailsSource).buildDetails(any());
assertThat(authentication).isNotNull();
}
@Test
public void convertWhenFormParameterIsPresentAndAllowFormEncodedBodyParameterThenConverted() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod(HttpMethod.POST.name());
request.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
request.addParameter("access_token", BEARER_TOKEN);
this.converter.setAllowFormEncodedBodyParameter(true);
assertThat(this.converter.convert(request)).isNotNull();
}
}

View File

@ -293,6 +293,16 @@ public class BearerTokenAuthenticationFilterTests {
// @formatter:on
}
@Test
public void setConverterWhenNullThenThrowsException() {
// @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