Browse Source

Add Support BearerTokenAuthenticationConverter

Closes gh-14750

Signed-off-by: Max Batischev <mblancer@mail.ru>
Max Batischev 4 months ago
parent
commit
4967f3feee
15 changed files with 721 additions and 57 deletions
  1. 76 19
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java
  2. 68 8
      config/src/main/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParser.java
  3. 3 0
      config/src/main/resources/org/springframework/security/config/spring-security-6.5.rnc
  4. 6 0
      config/src/main/resources/org/springframework/security/config/spring-security-6.5.xsd
  5. 76 7
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java
  6. 28 13
      config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java
  7. 32 0
      config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationConverter.xml
  8. 32 0
      config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationConverterAndBearerTokenResolver.xml
  9. 27 0
      config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockAuthenticationConverter.xml
  10. 4 0
      docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc
  11. 4 1
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenResolver.java
  12. 181 0
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationConverter.java
  13. 26 9
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java
  14. 148 0
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationConverterTests.java
  15. 10 0
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java

+ 76 - 19
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java

@@ -23,6 +23,8 @@ import java.util.Map;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 import jakarta.servlet.http.HttpServletRequest;
 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.context.ApplicationContext;
 import org.springframework.core.convert.converter.Converter;
 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.CsrfConfigurer;
 import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
 import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
 import org.springframework.security.config.http.SessionCreationPolicy;
 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.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 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.JwtAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
 import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
 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.introspection.SpringOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
 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.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.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.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.access.AccessDeniedHandler;
 import org.springframework.security.web.access.AccessDeniedHandler;
 import org.springframework.security.web.access.AccessDeniedHandlerImpl;
 import org.springframework.security.web.access.AccessDeniedHandlerImpl;
 import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
 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.csrf.CsrfException;
 import org.springframework.security.web.util.matcher.AndRequestMatcher;
 import org.springframework.security.web.util.matcher.AndRequestMatcher;
 import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
 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.RequestHeaderRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
 import org.springframework.web.accept.ContentNegotiationStrategy;
 import org.springframework.web.accept.ContentNegotiationStrategy;
 import org.springframework.web.accept.HeaderContentNegotiationStrategy;
 import org.springframework.web.accept.HeaderContentNegotiationStrategy;
 
 
@@ -156,7 +162,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 
 
 	private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
 	private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
 
 
-	private BearerTokenResolver bearerTokenResolver;
+	private AuthenticationConverter authenticationConverter;
 
 
 	private JwtConfigurer jwtConfigurer;
 	private JwtConfigurer jwtConfigurer;
 
 
@@ -194,9 +200,25 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 		return this;
 		return this;
 	}
 	}
 
 
+	/**
+	 * @deprecated please use {@link #authenticationConverter} instead
+	 */
+	@Deprecated
 	public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
 	public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
 		Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
 		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;
 		return this;
 	}
 	}
 
 
@@ -271,8 +293,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 
 
 	@Override
 	@Override
 	public void configure(H http) {
 	public void configure(H http) {
-		BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
-		this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
 		AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
 		AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
 		if (resolver == null) {
 		if (resolver == null) {
 			AuthenticationManager authenticationManager = getAuthenticationManager(http);
 			AuthenticationManager authenticationManager = getAuthenticationManager(http);
@@ -280,7 +300,9 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 		}
 		}
 
 
 		BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
 		BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
-		filter.setBearerTokenResolver(bearerTokenResolver);
+		AuthenticationConverter converter = getAuthenticationConverter();
+		this.requestMatcher.setAuthenticationConverter(converter);
+		filter.setAuthenticationConverter(converter);
 		filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
 		filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
 		filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
 		filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
 		filter = postProcess(filter);
 		filter = postProcess(filter);
@@ -363,16 +385,29 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 		return http.getSharedObject(AuthenticationManager.class);
 		return http.getSharedObject(AuthenticationManager.class);
 	}
 	}
 
 
+	AuthenticationConverter getAuthenticationConverter() {
+		if (this.authenticationConverter != null) {
+			return this.authenticationConverter;
+		}
+		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() {
 	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 authenticationConverter = getAuthenticationConverter();
+		if (authenticationConverter instanceof BearerTokenResolverAuthenticationConverterAdapter bearer) {
+			return bearer.bearerTokenResolver;
 		}
 		}
-		return this.bearerTokenResolver;
+		return null;
 	}
 	}
 
 
 	public class JwtConfigurer {
 	public class JwtConfigurer {
@@ -560,21 +595,43 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 
 
 	private static final class BearerTokenRequestMatcher implements RequestMatcher {
 	private static final class BearerTokenRequestMatcher implements RequestMatcher {
 
 
-		private BearerTokenResolver bearerTokenResolver;
+		private AuthenticationConverter authenticationConverter;
 
 
 		@Override
 		@Override
 		public boolean matches(HttpServletRequest request) {
 		public boolean matches(HttpServletRequest request) {
 			try {
 			try {
-				return this.bearerTokenResolver.resolve(request) != null;
+				return this.authenticationConverter.convert(request) != null;
 			}
 			}
 			catch (OAuth2AuthenticationException ex) {
 			catch (OAuth2AuthenticationException ex) {
 				return false;
 				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);
 		}
 		}
 
 
 	}
 	}

+ 68 - 8
config/src/main/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParser.java

@@ -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");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with 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.w3c.dom.Element;
 
 
 import org.springframework.beans.BeanMetadataElement;
 import org.springframework.beans.BeanMetadataElement;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
 import org.springframework.beans.factory.FactoryBean;
 import org.springframework.beans.factory.FactoryBean;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanReference;
 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.introspection.SpringOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
 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.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.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.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
+import org.springframework.security.web.authentication.AuthenticationConverter;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 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 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 ENTRY_POINT_REF = "entry-point-ref";
 
 
 	static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
 	static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
 
 
+	static final String AUTHENTICATION_CONVERTER = "authenticationConverter";
+
 	static final String AUTHENTICATION_ENTRY_POINT = "authenticationEntryPoint";
 	static final String AUTHENTICATION_ENTRY_POINT = "authenticationEntryPoint";
 
 
 	private final BeanReference authenticationManager;
 	private final BeanReference authenticationManager;
@@ -124,11 +130,16 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
 					pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
 					pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
 		}
 		}
 		BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
 		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);
 		BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
+		BeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);
 		this.entryPoints.put(requestMatcher, authenticationEntryPoint);
 		this.entryPoints.put(requestMatcher, authenticationEntryPoint);
 		this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
 		this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
 		this.ignoreCsrfRequestMatchers.add(requestMatcher);
 		this.ignoreCsrfRequestMatchers.add(requestMatcher);
@@ -136,13 +147,33 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
 			.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
 			.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
 		BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
 		BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
 		filterBuilder.addConstructorArgValue(authenticationManagerResolver);
 		filterBuilder.addConstructorArgValue(authenticationManagerResolver);
-		filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
 		filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
 		filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
 		filterBuilder.addPropertyValue("securityContextHolderStrategy",
 		filterBuilder.addPropertyValue("securityContextHolderStrategy",
 				this.authenticationFilterSecurityContextHolderStrategy);
 				this.authenticationFilterSecurityContextHolderStrategy);
+
+		if (authenticationConverter != null) {
+			filterBuilder.addPropertyValue(AUTHENTICATION_CONVERTER, authenticationConverter);
+		}
+		if (bearerTokenResolver != null) {
+			filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
+		}
 		return filterBuilder.getBeanDefinition();
 		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) {
 	void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
 		if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
 		if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
 			if (jwt == null && opaqueToken == null) {
 			if (jwt == null && opaqueToken == null) {
@@ -178,11 +209,19 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
 	BeanMetadataElement getBearerTokenResolver(Element element) {
 	BeanMetadataElement getBearerTokenResolver(Element element) {
 		String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
 		String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
 		if (!StringUtils.hasLength(bearerTokenResolverRef)) {
 		if (!StringUtils.hasLength(bearerTokenResolverRef)) {
-			return new RootBeanDefinition(DefaultBearerTokenResolver.class);
+			return null;
 		}
 		}
 		return new RuntimeBeanReference(bearerTokenResolverRef);
 		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) {
 	BeanMetadataElement getEntryPoint(Element element) {
 		String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
 		String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
 		if (!StringUtils.hasLength(entryPointRef)) {
 		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;
+			}
+		}
+
+	}
+
 }
 }

+ 3 - 0
config/src/main/resources/org/springframework/security/config/spring-security-6.5.rnc

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

+ 6 - 0
config/src/main/resources/org/springframework/security/config/spring-security-6.5.xsd

@@ -1999,6 +1999,12 @@
                 </xs:documentation>
                 </xs:documentation>
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </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:attributeGroup>
   <xs:element name="jwt">
   <xs:element name="jwt">
       <xs:annotation>
       <xs:annotation>

+ 76 - 7
config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java

@@ -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.BearerTokenResolver;
 import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
 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.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.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
 import org.springframework.security.provisioning.InMemoryUserDetailsManager;
 import org.springframework.security.provisioning.InMemoryUserDetailsManager;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.access.AccessDeniedHandler;
 import org.springframework.security.web.access.AccessDeniedHandler;
 import org.springframework.security.web.access.AccessDeniedHandlerImpl;
 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.MockMvc;
 import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.ResultMatcher;
 import org.springframework.test.web.servlet.ResultMatcher;
@@ -759,13 +761,6 @@ public class OAuth2ResourceServerConfigurerTests {
 		assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
 		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
 	@Test
 	public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
 	public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
 		this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
 		this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
@@ -1415,6 +1410,47 @@ public class OAuth2ResourceServerConfigurerTests {
 		verify(authenticationConverter).convert(any(), any());
 		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) {
 	private static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {
 		context.registerBean(name, clazz, () -> mock(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
 	@Configuration
 	@EnableWebSecurity
 	@EnableWebSecurity
 	static class MultipleIssuersConfig {
 	static class MultipleIssuersConfig {

+ 28 - 13
config/src/test/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests.java

@@ -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");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with 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.time.ZoneId;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Properties;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -50,13 +49,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mockito;
 import org.mockito.Mockito;
 import org.w3c.dom.Element;
 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.DisposableBean;
 import org.springframework.beans.factory.FactoryBean;
 import org.springframework.beans.factory.FactoryBean;
 import org.springframework.beans.factory.annotation.Autowired;
 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.parsing.BeanDefinitionParsingException;
-import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
 import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.beans.factory.xml.XmlReaderContext;
 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.JwtDecoder;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.jwt.TestJwts;
 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.authentication.JwtAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
 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.OpaqueTokenAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
 import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
 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.context.junit.jupiter.SpringExtension;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.MvcResult;
 import org.springframework.test.web.servlet.MvcResult;
@@ -462,6 +461,24 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 		verify(bearerTokenResolver).resolve(any(HttpServletRequest.class));
 		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
 	@Test
 	public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
 	public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
 			throws Exception {
 			throws Exception {
@@ -521,14 +538,6 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 		// @formatter:on
 		// @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
 	@Test
 	public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
 	public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
 		this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
 		this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
@@ -545,6 +554,12 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
 			.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
 			.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
 	}
 	}
 
 
+	@Test
+	public void configureWhenAuthenticationConverterAndJwkSetUriThenException() {
+		assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(
+				() -> this.spring.configLocations(xml("AuthenticationConverterAndBearerTokenResolver")).autowire());
+	}
+
 	@Test
 	@Test
 	public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
 	public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
 		this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();
 		this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();

+ 32 - 0
config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationConverter.xml

@@ -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>

+ 32 - 0
config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationConverterAndBearerTokenResolver.xml

@@ -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>

+ 27 - 0
config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockAuthenticationConverter.xml

@@ -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>

+ 4 - 0
docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc

@@ -1272,6 +1272,10 @@ Reference to a `BearerTokenResolver` which will retrieve the bearer token from t
 * **entry-point-ref**
 * **entry-point-ref**
 Reference to a `AuthenticationEntryPoint` which will handle unauthorized requests
 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]]
 [[nsa-jwt]]
 == <jwt>
 == <jwt>
 Represents an OAuth 2.0 Resource Server that will authorize JWTs
 Represents an OAuth 2.0 Resource Server that will authorize JWTs

+ 4 - 1
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenResolver.java

@@ -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");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with 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
  * @since 5.1
  * @see <a href="https://tools.ietf.org/html/rfc6750#section-2" target="_blank">RFC 6750
  * @see <a href="https://tools.ietf.org/html/rfc6750#section-2" target="_blank">RFC 6750
  * Section 2: Authenticated Requests</a>
  * Section 2: Authenticated Requests</a>
+ * @deprecated Use
+ * {@link org.springframework.security.web.authentication.AuthenticationConverter} instead
  */
  */
+@Deprecated
 @FunctionalInterface
 @FunctionalInterface
 public interface BearerTokenResolver {
 public interface BearerTokenResolver {
 
 

+ 181 - 0
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationConverter.java

@@ -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;
+	}
+
+}

+ 26 - 9
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java

@@ -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.BearerTokenResolver;
 import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
 import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
 import org.springframework.security.web.AuthenticationEntryPoint;
 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.AuthenticationEntryPointFailureHandler;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
 import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
 import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
@@ -83,12 +84,12 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
 	private AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
 	private AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
 			(request, response, exception) -> this.authenticationEntryPoint.commence(request, response, exception));
 			(request, response, exception) -> this.authenticationEntryPoint.commence(request, response, exception));
 
 
-	private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
-
 	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
 	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
 
 
 	private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
 	private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
 
 
+	private AuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
+
 	/**
 	/**
 	 * Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
 	 * Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)
 	 * @param authenticationManagerResolver
 	 * @param authenticationManagerResolver
@@ -121,24 +122,22 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
 	@Override
 	@Override
 	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
 			throws ServletException, IOException {
 			throws ServletException, IOException {
-		String token;
+		Authentication authenticationRequest;
 		try {
 		try {
-			token = this.bearerTokenResolver.resolve(request);
+			authenticationRequest = this.authenticationConverter.convert(request);
 		}
 		}
 		catch (OAuth2AuthenticationException invalid) {
 		catch (OAuth2AuthenticationException invalid) {
 			this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
 			this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
 			this.authenticationEntryPoint.commence(request, response, invalid);
 			this.authenticationEntryPoint.commence(request, response, invalid);
 			return;
 			return;
 		}
 		}
-		if (token == null) {
+
+		if (authenticationRequest == null) {
 			this.logger.trace("Did not process request since did not find bearer token");
 			this.logger.trace("Did not process request since did not find bearer token");
 			filterChain.doFilter(request, response);
 			filterChain.doFilter(request, response);
 			return;
 			return;
 		}
 		}
 
 
-		BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
-		authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
-
 		try {
 		try {
 			AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
 			AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
 			Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
 			Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
@@ -194,7 +193,14 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
 	 */
 	 */
 	public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
 	public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
 		Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
 		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);
 		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;
+	}
+
 }
 }

+ 148 - 0
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationConverterTests.java

@@ -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();
+	}
+
+}

+ 10 - 0
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java

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