Polish BearerTokenAuthenticationConverter Support

- Moved to BearerTokenAuthenticationFilter constructor to align with
AuthenticationFilter
- Undeprecated BearerTokenResolver to reduce number of migration scenarios
- Updated to 7.0 schema
- Added migration docs

Issue gh-14750
This commit is contained in:
Josh Cummings 2025-06-04 13:35:31 -06:00
parent 30577bd291
commit eaab42a73c
14 changed files with 203 additions and 376 deletions

View File

@ -23,8 +23,6 @@ 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;
@ -44,7 +42,6 @@ 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;
@ -69,7 +66,6 @@ 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;
@ -200,13 +196,9 @@ 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.authenticationConverter = new BearerTokenResolverAuthenticationConverterAdapter(bearerTokenResolver); this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
return this; return this;
} }
@ -214,7 +206,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
* Sets the {@link AuthenticationConverter} to use. * Sets the {@link AuthenticationConverter} to use.
* @param authenticationConverter the authentication converter * @param authenticationConverter the authentication converter
* @return the {@link OAuth2ResourceServerConfigurer} for further configuration * @return the {@link OAuth2ResourceServerConfigurer} for further configuration
* @since 6.5 * @since 7.0
*/ */
public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) { public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
@ -299,10 +291,9 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
resolver = (request) -> authenticationManager; resolver = (request) -> authenticationManager;
} }
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
AuthenticationConverter converter = getAuthenticationConverter(); AuthenticationConverter converter = getAuthenticationConverter();
this.requestMatcher.setAuthenticationConverter(converter); this.requestMatcher.setAuthenticationConverter(converter);
filter.setAuthenticationConverter(converter); BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver, converter);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint); filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
filter = postProcess(filter); filter = postProcess(filter);
@ -394,7 +385,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
} }
else if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) { else if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
BearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class); BearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
this.authenticationConverter = new BearerTokenResolverAuthenticationConverterAdapter(bearerTokenResolver); this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
} }
else { else {
this.authenticationConverter = new BearerTokenAuthenticationConverter(); this.authenticationConverter = new BearerTokenAuthenticationConverter();
@ -404,7 +395,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
BearerTokenResolver getBearerTokenResolver() { BearerTokenResolver getBearerTokenResolver() {
AuthenticationConverter authenticationConverter = getAuthenticationConverter(); AuthenticationConverter authenticationConverter = getAuthenticationConverter();
if (authenticationConverter instanceof BearerTokenResolverAuthenticationConverterAdapter bearer) { if (authenticationConverter instanceof OAuth2ResourceServerConfigurer.BearerTokenResolverHoldingAuthenticationConverter bearer) {
return bearer.bearerTokenResolver; return bearer.bearerTokenResolver;
} }
return null; return null;
@ -614,24 +605,22 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
} }
private static final class BearerTokenResolverAuthenticationConverterAdapter implements AuthenticationConverter { private static final class BearerTokenResolverHoldingAuthenticationConverter implements AuthenticationConverter {
private final Log logger = LogFactory.getLog(BearerTokenResolverAuthenticationConverterAdapter.class);
private final BearerTokenResolver bearerTokenResolver; private final BearerTokenResolver bearerTokenResolver;
BearerTokenResolverAuthenticationConverterAdapter(BearerTokenResolver bearerTokenResolver) { private final AuthenticationConverter authenticationConverter;
BearerTokenResolverHoldingAuthenticationConverter(BearerTokenResolver bearerTokenResolver) {
this.bearerTokenResolver = bearerTokenResolver; this.bearerTokenResolver = bearerTokenResolver;
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
authenticationConverter.setBearerTokenResolver(bearerTokenResolver);
this.authenticationConverter = authenticationConverter;
} }
@Override @Override
public Authentication convert(HttpServletRequest request) { public Authentication convert(HttpServletRequest request) {
String token = this.bearerTokenResolver.resolve(request); return this.authenticationConverter.convert(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

@ -72,8 +72,6 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
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;
@ -152,7 +150,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
this.authenticationFilterSecurityContextHolderStrategy); this.authenticationFilterSecurityContextHolderStrategy);
if (authenticationConverter != null) { if (authenticationConverter != null) {
filterBuilder.addPropertyValue(AUTHENTICATION_CONVERTER, authenticationConverter); filterBuilder.addConstructorArgValue(authenticationConverter);
} }
if (bearerTokenResolver != null) { if (bearerTokenResolver != null) {
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver); filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
@ -170,7 +168,9 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
} }
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
.rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class); .rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class);
requestMatcherBuilder.addConstructorArgValue(authenticationConverter); if (authenticationConverter != null) {
requestMatcherBuilder.addConstructorArgValue(authenticationConverter);
}
return requestMatcherBuilder.getBeanDefinition(); return requestMatcherBuilder.getBeanDefinition();
} }
@ -409,6 +409,10 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
private final AuthenticationConverter authenticationConverter; private final AuthenticationConverter authenticationConverter;
BearerTokenAuthenticationRequestMatcher() {
this.authenticationConverter = new BearerTokenAuthenticationConverter();
}
BearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) { BearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = authenticationConverter; this.authenticationConverter = authenticationConverter;

View File

@ -650,9 +650,6 @@ 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

View File

@ -1999,12 +1999,6 @@
</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>

View File

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

View File

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

View File

@ -2571,16 +2571,20 @@ public class OAuth2ResourceServerConfigurerTests {
@Bean @Bean
AuthenticationConverter authenticationConverterOne() { AuthenticationConverter authenticationConverterOne() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
converter.setAllowUriQueryParameter(true); resolver.setAllowUriQueryParameter(true);
return converter; BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
authenticationConverter.setBearerTokenResolver(resolver);
return authenticationConverter;
} }
@Bean @Bean
AuthenticationConverter authenticationConverterTwo() { AuthenticationConverter authenticationConverterTwo() {
BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
converter.setAllowUriQueryParameter(true); resolver.setAllowUriQueryParameter(true);
return converter; BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
authenticationConverter.setBearerTokenResolver(resolver);
return authenticationConverter;
} }
} }

View File

@ -115,3 +115,58 @@ fun authenticationConverter(val registrations: RelyingPartyRegistrationRepositor
====== ======
If you must continue using `Saml2AuthenticationTokenConverter`, `OpenSaml4AuthenticationTokenConverter`, or `OpenSaml5AuthenticationTokenConverter` to process GET requests, you can call `setShouldConvertGetRequests` to `true.` If you must continue using `Saml2AuthenticationTokenConverter`, `OpenSaml4AuthenticationTokenConverter`, or `OpenSaml5AuthenticationTokenConverter` to process GET requests, you can call `setShouldConvertGetRequests` to `true.`
== Provide an AuthenticationConverter to BearerTokenAuthenticationFilter
In Spring Security 7, `BearerTokenAuthenticationFilter#setBearerTokenResolver` and `#setAuthenticaionDetailsSource` are deprecated in favor of configuring those on `BearerTokenAuthenticationConverter`.
The `oauth2ResourceServer` DSL addresses most use cases and you need to nothing.
If you are setting a `BearerTokenResolver` or `AuthenticationDetailsSource` directly on `BearerTokenAuthenticationFilter` similar to the following:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(authenticationManager);
filter.setBearerTokenResolver(myBearerTokenResolver);
filter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
val filter = BearerTokenAuthenticationFilter(authenticationManager)
filter.setBearerTokenResolver(myBearerTokenResolver)
filter.setAuthenticationDetailsSource(myAuthenticationDetailsSource)
----
======
you are encouraged to use `BearerTokenAuthenticationConverter` to specify both:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
BearerTokenAuthenticationConverter authenticationConverter =
new BearerTokenAuthenticationConverter();
authenticationConverter.setBearerTokenResolver(myBearerTokenResolver);
authenticationConverter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(authenticationManager, authenicationConverter);
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
val authenticationConverter = BearerTokenAuthenticationConverter()
authenticationConverter.setBearerTokenResolver(myBearerTokenResolver)
authenticationConverter.setAuthenticationDetailsSource(myAuthenticationDetailsSource)
val filter = BearerTokenAuthenticationFilter(authenticationManager, authenticationConverter)
----
======

View File

@ -1266,7 +1266,8 @@ Reference to an `AuthenticationManagerResolver` which will resolve the `Authenti
[[nsa-oauth2-resource-server-bearer-token-resolver-ref]] [[nsa-oauth2-resource-server-bearer-token-resolver-ref]]
* **bearer-token-resolver-ref** * **bearer-token-resolver-ref**
Reference to a `BearerTokenResolver` which will retrieve the bearer token from the request Reference to a `BearerTokenResolver` which will retrieve the bearer token from the request.
This cannot be used in conjunction with `authentication-converter-ref`
[[nsa-oauth2-resource-server-entry-point-ref]] [[nsa-oauth2-resource-server-entry-point-ref]]
* **entry-point-ref** * **entry-point-ref**
@ -1274,7 +1275,8 @@ Reference to a `AuthenticationEntryPoint` which will handle unauthorized request
[[nsa-oauth2-resource-server-authentication-converter-ref]] [[nsa-oauth2-resource-server-authentication-converter-ref]]
* **authentication-converter-ref** * **authentication-converter-ref**
Reference to a `AuthenticationConverter` which convert request to authentication Reference to a `AuthenticationConverter` which convert request to authentication.
This cannot be used in conjunction with `bearer-token-resolver-ref`
[[nsa-jwt]] [[nsa-jwt]]
== <jwt> == <jwt>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2025 the original author or authors. * Copyright 2002-2020 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,10 +29,7 @@ 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 {

View File

@ -16,20 +16,13 @@
package org.springframework.security.oauth2.server.resource.web.authentication; 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 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.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication; 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.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -40,131 +33,29 @@ import org.springframework.util.StringUtils;
* {@link BearerTokenAuthenticationToken} * {@link BearerTokenAuthenticationToken}
* *
* @author Max Batischev * @author Max Batischev
* @since 6.5 * @author Josh Cummings
* @since 7.0
*/ */
public final class BearerTokenAuthenticationConverter implements AuthenticationConverter { public final class BearerTokenAuthenticationConverter implements AuthenticationConverter {
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource(); private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private static final Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-._~+/]+=*)$", private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
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 @Override
public Authentication convert(HttpServletRequest request) { public Authentication convert(HttpServletRequest request) {
String token = resolveToken(request); String token = this.bearerTokenResolver.resolve(request);
if (StringUtils.hasText(token)) { if (StringUtils.hasText(token)) {
BearerTokenAuthenticationToken authenticationToken = new BearerTokenAuthenticationToken(token); BearerTokenAuthenticationToken authenticationToken = new BearerTokenAuthenticationToken(token);
authenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request)); authenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));
return authenticationToken; return authenticationToken;
} }
return null; return null;
} }
private String resolveToken(HttpServletRequest request) { public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
final String authorizationHeaderToken = resolveFromAuthorizationHeader(request); Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
final String parameterToken = isParameterTokenSupportedForRequest(request) this.bearerTokenResolver = bearerTokenResolver;
? 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;
} }
/** /**

View File

@ -40,6 +40,7 @@ import org.springframework.security.oauth2.server.resource.BearerTokenErrors;
import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.AbstractOAuth2TokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken; import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
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.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.DefaultBearerTokenResolver;
@ -76,6 +77,8 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver; private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private final AuthenticationConverter authenticationConverter;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy(); .getContextHolderStrategy();
@ -84,20 +87,15 @@ 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 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
*/ */
public BearerTokenAuthenticationFilter( public BearerTokenAuthenticationFilter(
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) { AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null"); this(authenticationManagerResolver, new BearerTokenAuthenticationConverter());
this.authenticationManagerResolver = authenticationManagerResolver;
} }
/** /**
@ -105,8 +103,43 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
* @param authenticationManager * @param authenticationManager
*/ */
public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager) { public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager) {
this(authenticationManager, new BearerTokenAuthenticationConverter());
}
/**
* Construct this filter using the provided parameters
* @param authenticationManager the {@link AuthenticationManager} to use
* @param authenticationConverter the {@link AuthenticationConverter} to use
* @since 7.0
* @see JwtAuthenticationProvider
* @see OpaqueTokenAuthenticationProvider
* @see BearerTokenAuthenticationConverter
*/
public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager,
AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.notNull(authenticationManager, "authenticationManager cannot be null");
this.authenticationManagerResolver = (request) -> authenticationManager; Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationManagerResolver = (authentication) -> authenticationManager;
this.authenticationConverter = authenticationConverter;
}
/**
* Construct this filter using the provided parameters
* @param authenticationManagerResolver the {@link AuthenticationManagerResolver} to
* use
* @param authenticationConverter the {@link AuthenticationConverter} to use
* @since 7.0
* @see JwtAuthenticationProvider
* @see OpaqueTokenAuthenticationProvider
* @see BearerTokenAuthenticationConverter
*/
public BearerTokenAuthenticationFilter(
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,
AuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationManagerResolver = authenticationManagerResolver;
this.authenticationConverter = authenticationConverter;
} }
/** /**
@ -190,17 +223,20 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
* Set the {@link BearerTokenResolver} to use. Defaults to * Set the {@link BearerTokenResolver} to use. Defaults to
* {@link DefaultBearerTokenResolver}. * {@link DefaultBearerTokenResolver}.
* @param bearerTokenResolver the {@code BearerTokenResolver} to use * @param bearerTokenResolver the {@code BearerTokenResolver} to use
* @deprecated Please provide an {@link AuthenticationConverter} in the constructor
* instead
* @see BearerTokenAuthenticationConverter
*/ */
@Deprecated
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.authenticationConverter = (request) -> { if (this.authenticationConverter instanceof BearerTokenAuthenticationConverter converter) {
String token = bearerTokenResolver.resolve(request); converter.setBearerTokenResolver(bearerTokenResolver);
if (!StringUtils.hasText(token)) { }
this.logger.trace("Did not process request since did not find bearer token"); else {
return null; throw new IllegalArgumentException(
} "You cannot both specify an AuthenticationConverter and a BearerTokenResolver.");
return new BearerTokenAuthenticationToken(token); }
};
} }
/** /**
@ -227,13 +263,24 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
/** /**
* Set the {@link AuthenticationDetailsSource} to use. Defaults to * Set the {@link AuthenticationDetailsSource} to use. Defaults to
* {@link WebAuthenticationDetailsSource}. * {@link WebAuthenticationDetailsSource}.
* @param authenticationDetailsSource the {@code AuthenticationConverter} to use * @param authenticationDetailsSource the {@code AuthenticationDetailsSource} to use
* @since 5.5 * @since 5.5
* @deprecated Please provide an {@link AuthenticationConverter} in the constructor
* and set the {@link AuthenticationDetailsSource} there instead. For example, you can
* use {@link BearerTokenAuthenticationConverter#setAuthenticationDetailsSource}
* @see BearerTokenAuthenticationConverter
*/ */
@Deprecated
public void setAuthenticationDetailsSource( public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) { AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
this.authenticationDetailsSource = authenticationDetailsSource; if (this.authenticationConverter instanceof BearerTokenAuthenticationConverter converter) {
converter.setAuthenticationDetailsSource(authenticationDetailsSource);
}
else {
throw new IllegalArgumentException(
"You cannot specify both an AuthenticationConverter and an AuthenticationDetailsSource");
}
} }
private static boolean isDPoPBoundAccessToken(Authentication authentication) { private static boolean isDPoPBoundAccessToken(Authentication authentication) {
@ -249,15 +296,4 @@ 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;
}
} }

View File

@ -27,6 +27,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication; 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.server.resource.web.DefaultBearerTokenResolver;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -46,8 +47,14 @@ public class BearerTokenAuthenticationConverterTests {
private static final String BEARER_TOKEN = "test_bearer_token"; private static final String BEARER_TOKEN = "test_bearer_token";
private final DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
private final BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter(); private final BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
{
this.converter.setBearerTokenResolver(this.resolver);
}
@Test @Test
public void convertWhenAuthorizationHeaderIsPresentThenTokenIsConverted() { public void convertWhenAuthorizationHeaderIsPresentThenTokenIsConverted() {
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
@ -64,7 +71,7 @@ public class BearerTokenAuthenticationConverterTests {
request.setMethod(HttpMethod.GET.name()); request.setMethod(HttpMethod.GET.name());
request.addParameter("access_token", BEARER_TOKEN); request.addParameter("access_token", BEARER_TOKEN);
this.converter.setAllowUriQueryParameter(true); this.resolver.setAllowUriQueryParameter(true);
Authentication authentication = this.converter.convert(request); Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNotNull(); assertThat(authentication).isNotNull();
@ -86,6 +93,7 @@ public class BearerTokenAuthenticationConverterTests {
request.setMethod(HttpMethod.GET.name()); request.setMethod(HttpMethod.GET.name());
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN); request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN);
this.resolver.setAllowUriQueryParameter(true);
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request)) assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))
.withMessageContaining("Found multiple bearer tokens in the request"); .withMessageContaining("Found multiple bearer tokens in the request");
} }
@ -95,7 +103,7 @@ public class BearerTokenAuthenticationConverterTests {
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader(X_AUTH_TOKEN_HEADER, "Bearer " + TEST_X_AUTH_TOKEN); request.addHeader(X_AUTH_TOKEN_HEADER, "Bearer " + TEST_X_AUTH_TOKEN);
this.converter.setBearerTokenHeaderName(X_AUTH_TOKEN_HEADER); this.resolver.setBearerTokenHeaderName(X_AUTH_TOKEN_HEADER);
Authentication authentication = this.converter.convert(request); Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNotNull(); assertThat(authentication).isNotNull();
@ -140,7 +148,7 @@ public class BearerTokenAuthenticationConverterTests {
request.setMethod(HttpMethod.POST.name()); request.setMethod(HttpMethod.POST.name());
request.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE); request.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
request.addParameter("access_token", BEARER_TOKEN); request.addParameter("access_token", BEARER_TOKEN);
this.converter.setAllowFormEncodedBodyParameter(true); this.resolver.setAllowFormEncodedBodyParameter(true);
assertThat(this.converter.convert(request)).isNotNull(); assertThat(this.converter.convert(request)).isNotNull();
} }

View File

@ -75,8 +75,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
public class BearerTokenAuthenticationFilterTests { public class BearerTokenAuthenticationFilterTests {
private static final String TEST_TOKEN = "token";
@Mock @Mock
AuthenticationEntryPoint authenticationEntryPoint; AuthenticationEntryPoint authenticationEntryPoint;
@ -95,9 +93,6 @@ public class BearerTokenAuthenticationFilterTests {
@Mock @Mock
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource; AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
@Mock
AuthenticationConverter authenticationConverter;
MockHttpServletRequest request; MockHttpServletRequest request;
MockHttpServletResponse response; MockHttpServletResponse response;
@ -269,6 +264,24 @@ public class BearerTokenAuthenticationFilterTests {
assertThat(error.getDescription()).isEqualTo("Invalid bearer token"); assertThat(error.getDescription()).isEqualTo("Invalid bearer token");
} }
@Test
public void doFilterWhenSetAuthenticationConverterAndAuthenticationDetailsSourceThenIllegalArgument(
@Mock AuthenticationConverter authenticationConverter) {
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager,
authenticationConverter);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> filter.setAuthenticationDetailsSource(this.authenticationDetailsSource));
}
@Test
public void doFilterWhenSetBearerTokenResolverAndAuthenticationConverterThenIllegalArgument(
@Mock AuthenticationConverter authenticationConverter) {
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager,
authenticationConverter);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> filter.setBearerTokenResolver(this.bearerTokenResolver));
}
@Test @Test
public void setAuthenticationEntryPointWhenNullThenThrowsException() { public void setAuthenticationEntryPointWhenNullThenThrowsException() {
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager); BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
@ -302,9 +315,8 @@ public class BearerTokenAuthenticationFilterTests {
@Test @Test
public void setConverterWhenNullThenThrowsException() { public void setConverterWhenNullThenThrowsException() {
// @formatter:off // @formatter:off
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy(() -> filter.setAuthenticationConverter(null)) .isThrownBy(() -> new BearerTokenAuthenticationFilter(this.authenticationManager, null))
.withMessageContaining("authenticationConverter cannot be null"); .withMessageContaining("authenticationConverter cannot be null");
// @formatter:on // @formatter:on
} }
@ -327,171 +339,6 @@ public class BearerTokenAuthenticationFilterTests {
// @formatter:on // @formatter:on
} }
@Test
public void doFilterWhenBearerTokenPresentAndConverterSetThenAuthenticates() throws ServletException, IOException {
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.doFilter(this.request, this.response, this.filterChain);
ArgumentCaptor<BearerTokenAuthenticationToken> captor = ArgumentCaptor
.forClass(BearerTokenAuthenticationToken.class);
verify(this.authenticationManager).authenticate(captor.capture());
assertThat(captor.getValue().getPrincipal()).isEqualTo(TEST_TOKEN);
assertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
.isNotNull();
}
@Test
public void doFilterWhenSecurityContextRepositoryAndConverterSetThenSaves() throws ServletException, IOException {
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
TestingAuthenticationToken expectedAuthentication = new TestingAuthenticationToken("test", "password");
given(this.authenticationManager.authenticate(any())).willReturn(expectedAuthentication);
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.setSecurityContextRepository(securityContextRepository);
filter.doFilter(this.request, this.response, this.filterChain);
ArgumentCaptor<BearerTokenAuthenticationToken> captor = ArgumentCaptor
.forClass(BearerTokenAuthenticationToken.class);
verify(this.authenticationManager).authenticate(captor.capture());
assertThat(captor.getValue().getPrincipal()).isEqualTo(TEST_TOKEN);
ArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);
verify(securityContextRepository).saveContext(contextArg.capture(), eq(this.request), eq(this.response));
assertThat(contextArg.getValue().getAuthentication().getName()).isEqualTo(expectedAuthentication.getName());
}
@Test
public void doFilterWhenUsingAuthenticationManagerResolverAndConverterSetThenAuthenticates() throws Exception {
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManagerResolver));
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
given(this.authenticationManagerResolver.resolve(any())).willReturn(this.authenticationManager);
filter.doFilter(this.request, this.response, this.filterChain);
ArgumentCaptor<BearerTokenAuthenticationToken> captor = ArgumentCaptor
.forClass(BearerTokenAuthenticationToken.class);
verify(this.authenticationManager).authenticate(captor.capture());
assertThat(captor.getValue().getPrincipal()).isEqualTo(TEST_TOKEN);
assertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
.isNotNull();
}
@Test
public void doFilterWhenNoBearerTokenPresentAndConverterSetThenDoesNotAuthenticate()
throws ServletException, IOException {
given(this.authenticationConverter.convert(this.request)).willReturn(null);
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.doFilter(this.request, this.response, this.filterChain);
verifyNoMoreInteractions(this.authenticationManager);
}
@Test
public void doFilterWhenMalformedBearerTokenAndConverterSetThenPropagatesError()
throws ServletException, IOException {
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST,
"description", "uri");
OAuth2AuthenticationException exception = new OAuth2AuthenticationException(error);
given(this.authenticationConverter.convert(this.request)).willThrow(exception);
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.doFilter(this.request, this.response, this.filterChain);
verifyNoMoreInteractions(this.authenticationManager);
verify(this.authenticationEntryPoint).commence(this.request, this.response, exception);
}
@Test
public void doFilterWhenAuthenticationFailsWithDefaultHandlerAndConverterSetThenPropagatesError()
throws ServletException, IOException {
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED,
"description", "uri");
OAuth2AuthenticationException exception = new OAuth2AuthenticationException(error);
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
given(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class))).willThrow(exception);
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.doFilter(this.request, this.response, this.filterChain);
verify(this.authenticationEntryPoint).commence(this.request, this.response, exception);
}
@Test
public void doFilterWhenAuthenticationFailsWithCustomHandlerAndConverterSetThenPropagatesError()
throws ServletException, IOException {
BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED,
"description", "uri");
OAuth2AuthenticationException exception = new OAuth2AuthenticationException(error);
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
given(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class))).willThrow(exception);
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
filter.setAuthenticationFailureHandler(this.authenticationFailureHandler);
filter.doFilter(this.request, this.response, this.filterChain);
verify(this.authenticationFailureHandler).onAuthenticationFailure(this.request, this.response, exception);
}
@Test
public void doFilterWhenConverterSetAndAuthenticationServiceExceptionThenRethrows() {
AuthenticationServiceException exception = new AuthenticationServiceException("message");
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
given(this.authenticationManager.authenticate(any())).willThrow(exception);
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
assertThatExceptionOfType(AuthenticationServiceException.class)
.isThrownBy(() -> filter.doFilter(this.request, this.response, this.filterChain));
}
@Test
public void doFilterWhenConverterSetAndCustomEntryPointAndAuthenticationErrorThenUses()
throws ServletException, IOException {
AuthenticationException exception = new InvalidBearerTokenException("message");
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
given(this.authenticationManager.authenticate(any())).willThrow(exception);
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
AuthenticationEntryPoint entrypoint = mock(AuthenticationEntryPoint.class);
filter.setAuthenticationEntryPoint(entrypoint);
filter.doFilter(this.request, this.response, this.filterChain);
verify(entrypoint).commence(any(), any(), any(InvalidBearerTokenException.class));
}
@Test
public void doFilterWhenConverterSetCustomSecurityContextHolderStrategyThenUses()
throws ServletException, IOException {
given(this.authenticationConverter.convert(this.request))
.willReturn(new BearerTokenAuthenticationToken(TEST_TOKEN));
BearerTokenAuthenticationFilter filter = addMocksWithConverter(
new BearerTokenAuthenticationFilter(this.authenticationManager));
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
given(strategy.createEmptyContext()).willReturn(new SecurityContextImpl());
filter.setSecurityContextHolderStrategy(strategy);
filter.doFilter(this.request, this.response, this.filterChain);
verify(strategy).setContext(any());
}
private BearerTokenAuthenticationFilter addMocks(BearerTokenAuthenticationFilter filter) { private BearerTokenAuthenticationFilter addMocks(BearerTokenAuthenticationFilter filter) {
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint); filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter.setBearerTokenResolver(this.bearerTokenResolver); filter.setBearerTokenResolver(this.bearerTokenResolver);
@ -506,10 +353,4 @@ public class BearerTokenAuthenticationFilterTests {
verifyNoMoreInteractions(this.authenticationManager); verifyNoMoreInteractions(this.authenticationManager);
} }
private BearerTokenAuthenticationFilter addMocksWithConverter(BearerTokenAuthenticationFilter filter) {
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter.setAuthenticationConverter(this.authenticationConverter);
return filter;
}
} }