mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-24 11:13:30 +00:00
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:
parent
30577bd291
commit
eaab42a73c
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
----
|
||||||
|
======
|
||||||
|
@ -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>
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user