mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-25 21:42:17 +00:00
Bump io.spring.develocity.conventions from 0.0.22 to 0.0.23
This commit is contained in:
commit
3948440ee4
@ -26,6 +26,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
@ -58,7 +59,9 @@ class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
|
|||||||
resolvers.add(new AccessDeniedExceptionResolver());
|
resolvers.add(new AccessDeniedExceptionResolver());
|
||||||
}
|
}
|
||||||
|
|
||||||
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor {
|
static class WebTargetVisitor implements AuthorizationAdvisorProxyFactory.TargetVisitor, Ordered {
|
||||||
|
|
||||||
|
private static final int DEFAULT_ORDER = 100;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
|
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
|
||||||
@ -81,6 +84,11 @@ class AuthorizationProxyWebConfiguration implements WebMvcConfigurer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return DEFAULT_ORDER;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AccessDeniedExceptionResolver implements HandlerExceptionResolver {
|
static class AccessDeniedExceptionResolver implements HandlerExceptionResolver {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -83,10 +83,11 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration
|
|||||||
|
|
||||||
private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
|
private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
|
||||||
|
|
||||||
@Autowired(required = false)
|
ReactiveAuthorizationManagerMethodSecurityConfiguration(
|
||||||
ReactiveAuthorizationManagerMethodSecurityConfiguration(MethodSecurityExpressionHandler expressionHandler,
|
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlers,
|
||||||
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor,
|
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor,
|
||||||
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) {
|
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) {
|
||||||
|
MethodSecurityExpressionHandler expressionHandler = expressionHandlers.getIfUnique();
|
||||||
if (expressionHandler != null) {
|
if (expressionHandler != null) {
|
||||||
this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
||||||
this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);
|
this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);
|
||||||
|
@ -29,6 +29,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
||||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -51,6 +52,9 @@ import org.springframework.security.web.context.RequestAttributeSecurityContextR
|
|||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Demonstrating Proof of Possession
|
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Demonstrating Proof of Possession
|
||||||
@ -76,7 +80,7 @@ final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
|
|||||||
@Override
|
@Override
|
||||||
public void configure(B http) {
|
public void configure(B http) {
|
||||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||||
http.authenticationProvider(new DPoPAuthenticationProvider(authenticationManager));
|
http.authenticationProvider(new DPoPAuthenticationProvider(getTokenAuthenticationManager(http)));
|
||||||
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager,
|
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager,
|
||||||
getAuthenticationConverter());
|
getAuthenticationConverter());
|
||||||
authenticationFilter.setRequestMatcher(getRequestMatcher());
|
authenticationFilter.setRequestMatcher(getRequestMatcher());
|
||||||
@ -87,6 +91,23 @@ final class DPoPAuthenticationConfigurer<B extends HttpSecurityBuilder<B>>
|
|||||||
http.addFilter(authenticationFilter);
|
http.addFilter(authenticationFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AuthenticationManager getTokenAuthenticationManager(B http) {
|
||||||
|
OAuth2ResourceServerConfigurer<B> resourceServerConfigurer = http
|
||||||
|
.getConfigurer(OAuth2ResourceServerConfigurer.class);
|
||||||
|
final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver = resourceServerConfigurer
|
||||||
|
.getAuthenticationManagerResolver();
|
||||||
|
if (authenticationManagerResolver == null) {
|
||||||
|
return resourceServerConfigurer.getAuthenticationManager(http);
|
||||||
|
}
|
||||||
|
return (authentication) -> {
|
||||||
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
|
||||||
|
AuthenticationManager authenticationManager = authenticationManagerResolver
|
||||||
|
.resolve(servletRequestAttributes.getRequest());
|
||||||
|
return authenticationManager.authenticate(authentication);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private RequestMatcher getRequestMatcher() {
|
private RequestMatcher getRequestMatcher() {
|
||||||
if (this.requestMatcher == null) {
|
if (this.requestMatcher == null) {
|
||||||
this.requestMatcher = new DPoPRequestMatcher();
|
this.requestMatcher = new DPoPRequestMatcher();
|
||||||
|
@ -37,6 +37,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
|||||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
@ -49,13 +50,14 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT
|
|||||||
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
|
||||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||||
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.security.web.csrf.CsrfException;
|
import org.springframework.security.web.csrf.CsrfException;
|
||||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||||
@ -156,7 +158,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||||||
|
|
||||||
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
|
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
|
||||||
|
|
||||||
private BearerTokenResolver bearerTokenResolver;
|
private AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
private JwtConfigurer jwtConfigurer;
|
private JwtConfigurer jwtConfigurer;
|
||||||
|
|
||||||
@ -196,7 +198,19 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||||||
|
|
||||||
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
|
public OAuth2ResourceServerConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
|
||||||
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
|
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
|
||||||
this.bearerTokenResolver = bearerTokenResolver;
|
this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link AuthenticationConverter} to use.
|
||||||
|
* @param authenticationConverter the authentication converter
|
||||||
|
* @return the {@link OAuth2ResourceServerConfigurer} for further configuration
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public OAuth2ResourceServerConfigurer<H> authenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||||
|
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||||
|
this.authenticationConverter = authenticationConverter;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,16 +285,15 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(H http) {
|
public void configure(H http) {
|
||||||
BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
|
|
||||||
this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
|
|
||||||
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
|
AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
|
||||||
if (resolver == null) {
|
if (resolver == null) {
|
||||||
AuthenticationManager authenticationManager = getAuthenticationManager(http);
|
AuthenticationManager authenticationManager = getAuthenticationManager(http);
|
||||||
resolver = (request) -> authenticationManager;
|
resolver = (request) -> authenticationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
|
AuthenticationConverter converter = getAuthenticationConverter();
|
||||||
filter.setBearerTokenResolver(bearerTokenResolver);
|
this.requestMatcher.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);
|
||||||
@ -363,16 +376,33 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||||||
return http.getSharedObject(AuthenticationManager.class);
|
return http.getSharedObject(AuthenticationManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
BearerTokenResolver getBearerTokenResolver() {
|
AuthenticationManagerResolver<HttpServletRequest> getAuthenticationManagerResolver() {
|
||||||
if (this.bearerTokenResolver == null) {
|
return this.authenticationManagerResolver;
|
||||||
if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
|
}
|
||||||
this.bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
|
|
||||||
}
|
AuthenticationConverter getAuthenticationConverter() {
|
||||||
else {
|
if (this.authenticationConverter != null) {
|
||||||
this.bearerTokenResolver = new DefaultBearerTokenResolver();
|
return this.authenticationConverter;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this.bearerTokenResolver;
|
if (this.context.getBeanNamesForType(AuthenticationConverter.class).length > 0) {
|
||||||
|
this.authenticationConverter = this.context.getBean(AuthenticationConverter.class);
|
||||||
|
}
|
||||||
|
else if (this.context.getBeanNamesForType(BearerTokenResolver.class).length > 0) {
|
||||||
|
BearerTokenResolver bearerTokenResolver = this.context.getBean(BearerTokenResolver.class);
|
||||||
|
this.authenticationConverter = new BearerTokenResolverHoldingAuthenticationConverter(bearerTokenResolver);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
}
|
||||||
|
return this.authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
BearerTokenResolver getBearerTokenResolver() {
|
||||||
|
AuthenticationConverter authenticationConverter = getAuthenticationConverter();
|
||||||
|
if (authenticationConverter instanceof OAuth2ResourceServerConfigurer.BearerTokenResolverHoldingAuthenticationConverter bearer) {
|
||||||
|
return bearer.bearerTokenResolver;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class JwtConfigurer {
|
public class JwtConfigurer {
|
||||||
@ -560,21 +590,41 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
|||||||
|
|
||||||
private static final class BearerTokenRequestMatcher implements RequestMatcher {
|
private static final class BearerTokenRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
private BearerTokenResolver bearerTokenResolver;
|
private AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(HttpServletRequest request) {
|
public boolean matches(HttpServletRequest request) {
|
||||||
try {
|
try {
|
||||||
return this.bearerTokenResolver.resolve(request) != null;
|
return this.authenticationConverter.convert(request) != null;
|
||||||
}
|
}
|
||||||
catch (OAuth2AuthenticationException ex) {
|
catch (OAuth2AuthenticationException ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBearerTokenResolver(BearerTokenResolver tokenResolver) {
|
void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||||
Assert.notNull(tokenResolver, "resolver cannot be null");
|
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||||
this.bearerTokenResolver = tokenResolver;
|
this.authenticationConverter = authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class BearerTokenResolverHoldingAuthenticationConverter implements AuthenticationConverter {
|
||||||
|
|
||||||
|
private final BearerTokenResolver bearerTokenResolver;
|
||||||
|
|
||||||
|
private final AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
|
BearerTokenResolverHoldingAuthenticationConverter(BearerTokenResolver bearerTokenResolver) {
|
||||||
|
this.bearerTokenResolver = bearerTokenResolver;
|
||||||
|
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
authenticationConverter.setBearerTokenResolver(bearerTokenResolver);
|
||||||
|
this.authenticationConverter = authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication convert(HttpServletRequest request) {
|
||||||
|
return this.authenticationConverter.convert(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import org.springframework.beans.BeanMetadataElement;
|
import org.springframework.beans.BeanMetadataElement;
|
||||||
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanReference;
|
import org.springframework.beans.factory.config.BeanReference;
|
||||||
@ -43,9 +44,10 @@ import org.springframework.security.oauth2.server.resource.authentication.Opaque
|
|||||||
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
|
||||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@ -64,6 +66,8 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||||||
|
|
||||||
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
|
static final String BEARER_TOKEN_RESOLVER_REF = "bearer-token-resolver-ref";
|
||||||
|
|
||||||
|
static final String AUTHENTICATION_CONVERTER_REF = "authentication-converter-ref";
|
||||||
|
|
||||||
static final String ENTRY_POINT_REF = "entry-point-ref";
|
static final String ENTRY_POINT_REF = "entry-point-ref";
|
||||||
|
|
||||||
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
|
static final String BEARER_TOKEN_RESOLVER = "bearerTokenResolver";
|
||||||
@ -124,11 +128,16 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||||||
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
|
pc.getReaderContext().registerWithGeneratedName(opaqueTokenAuthenticationProvider)));
|
||||||
}
|
}
|
||||||
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
|
BeanMetadataElement bearerTokenResolver = getBearerTokenResolver(oauth2ResourceServer);
|
||||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
BeanMetadataElement authenticationConverter = getAuthenticationConverter(oauth2ResourceServer);
|
||||||
.rootBeanDefinition(BearerTokenRequestMatcher.class);
|
if (bearerTokenResolver != null && authenticationConverter != null) {
|
||||||
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
|
throw new BeanDefinitionStoreException(
|
||||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
"You cannot use bearer-token-ref and authentication-converter-ref in the same oauth2-resource-server element");
|
||||||
|
}
|
||||||
|
if (bearerTokenResolver == null && authenticationConverter == null) {
|
||||||
|
authenticationConverter = new RootBeanDefinition(BearerTokenAuthenticationConverter.class);
|
||||||
|
}
|
||||||
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
|
BeanMetadataElement authenticationEntryPoint = getEntryPoint(oauth2ResourceServer);
|
||||||
|
BeanDefinition requestMatcher = buildRequestMatcher(bearerTokenResolver, authenticationConverter);
|
||||||
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
|
this.entryPoints.put(requestMatcher, authenticationEntryPoint);
|
||||||
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
|
this.deniedHandlers.put(requestMatcher, this.accessDeniedHandler);
|
||||||
this.ignoreCsrfRequestMatchers.add(requestMatcher);
|
this.ignoreCsrfRequestMatchers.add(requestMatcher);
|
||||||
@ -136,13 +145,35 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||||||
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
|
.rootBeanDefinition(BearerTokenAuthenticationFilter.class);
|
||||||
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
|
BeanMetadataElement authenticationManagerResolver = getAuthenticationManagerResolver(oauth2ResourceServer);
|
||||||
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
|
filterBuilder.addConstructorArgValue(authenticationManagerResolver);
|
||||||
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
|
|
||||||
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
|
filterBuilder.addPropertyValue(AUTHENTICATION_ENTRY_POINT, authenticationEntryPoint);
|
||||||
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
filterBuilder.addPropertyValue("securityContextHolderStrategy",
|
||||||
this.authenticationFilterSecurityContextHolderStrategy);
|
this.authenticationFilterSecurityContextHolderStrategy);
|
||||||
|
|
||||||
|
if (authenticationConverter != null) {
|
||||||
|
filterBuilder.addConstructorArgValue(authenticationConverter);
|
||||||
|
}
|
||||||
|
if (bearerTokenResolver != null) {
|
||||||
|
filterBuilder.addPropertyValue(BEARER_TOKEN_RESOLVER, bearerTokenResolver);
|
||||||
|
}
|
||||||
return filterBuilder.getBeanDefinition();
|
return filterBuilder.getBeanDefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BeanDefinition buildRequestMatcher(BeanMetadataElement bearerTokenResolver,
|
||||||
|
BeanMetadataElement authenticationConverter) {
|
||||||
|
if (bearerTokenResolver != null) {
|
||||||
|
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(BearerTokenRequestMatcher.class);
|
||||||
|
requestMatcherBuilder.addConstructorArgValue(bearerTokenResolver);
|
||||||
|
return requestMatcherBuilder.getBeanDefinition();
|
||||||
|
}
|
||||||
|
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||||
|
.rootBeanDefinition(BearerTokenAuthenticationRequestMatcher.class);
|
||||||
|
if (authenticationConverter != null) {
|
||||||
|
requestMatcherBuilder.addConstructorArgValue(authenticationConverter);
|
||||||
|
}
|
||||||
|
return requestMatcherBuilder.getBeanDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
|
void validateConfiguration(Element oauth2ResourceServer, Element jwt, Element opaqueToken, ParserContext pc) {
|
||||||
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
|
if (!oauth2ResourceServer.hasAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF)) {
|
||||||
if (jwt == null && opaqueToken == null) {
|
if (jwt == null && opaqueToken == null) {
|
||||||
@ -178,11 +209,19 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||||||
BeanMetadataElement getBearerTokenResolver(Element element) {
|
BeanMetadataElement getBearerTokenResolver(Element element) {
|
||||||
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
|
String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF);
|
||||||
if (!StringUtils.hasLength(bearerTokenResolverRef)) {
|
if (!StringUtils.hasLength(bearerTokenResolverRef)) {
|
||||||
return new RootBeanDefinition(DefaultBearerTokenResolver.class);
|
return null;
|
||||||
}
|
}
|
||||||
return new RuntimeBeanReference(bearerTokenResolverRef);
|
return new RuntimeBeanReference(bearerTokenResolverRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeanMetadataElement getAuthenticationConverter(Element element) {
|
||||||
|
String authenticationConverterRef = element.getAttribute(AUTHENTICATION_CONVERTER_REF);
|
||||||
|
if (!StringUtils.hasLength(authenticationConverterRef)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new RuntimeBeanReference(authenticationConverterRef);
|
||||||
|
}
|
||||||
|
|
||||||
BeanMetadataElement getEntryPoint(Element element) {
|
BeanMetadataElement getEntryPoint(Element element) {
|
||||||
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
|
String entryPointRef = element.getAttribute(ENTRY_POINT_REF);
|
||||||
if (!StringUtils.hasLength(entryPointRef)) {
|
if (!StringUtils.hasLength(entryPointRef)) {
|
||||||
@ -366,4 +405,29 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class BearerTokenAuthenticationRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
|
private final AuthenticationConverter authenticationConverter;
|
||||||
|
|
||||||
|
BearerTokenAuthenticationRequestMatcher() {
|
||||||
|
this.authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
BearerTokenAuthenticationRequestMatcher(AuthenticationConverter authenticationConverter) {
|
||||||
|
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||||
|
this.authenticationConverter = authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
return this.authenticationConverter.convert(request) != null;
|
||||||
|
}
|
||||||
|
catch (OAuth2AuthenticationException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -88,6 +88,7 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
|
|||||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
@ -127,12 +128,14 @@ import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthen
|
|||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.ResultMatcher;
|
import org.springframework.test.web.servlet.ResultMatcher;
|
||||||
@ -759,13 +762,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||||||
assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
|
assertThat(oauth2.getBearerTokenResolver()).isEqualTo(resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() {
|
|
||||||
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
|
|
||||||
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
|
||||||
assertThat(oauth2.getBearerTokenResolver()).isInstanceOf(DefaultBearerTokenResolver.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
|
public void requestWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
|
||||||
this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
|
this.spring.register(CustomAuthenticationDetailsSource.class, JwtDecoderConfig.class, BasicController.class)
|
||||||
@ -1415,6 +1411,47 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||||||
verify(authenticationConverter).convert(any(), any());
|
verify(authenticationConverter).convert(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenDuplicateConverterBeansAndAnotherOnTheDslThenTheDslOneIsUsed() {
|
||||||
|
AuthenticationConverter converter = mock(AuthenticationConverter.class);
|
||||||
|
AuthenticationConverter converterBean = mock(AuthenticationConverter.class);
|
||||||
|
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||||
|
context.registerBean("converterOne", AuthenticationConverter.class, () -> converterBean);
|
||||||
|
context.registerBean("converterTwo", AuthenticationConverter.class, () -> converterBean);
|
||||||
|
this.spring.context(context).autowire();
|
||||||
|
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
||||||
|
oauth2.authenticationConverter(converter);
|
||||||
|
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenConverterBeanAndAnotherOnTheDslThenTheDslOneIsUsed() {
|
||||||
|
AuthenticationConverter converter = mock(AuthenticationConverter.class);
|
||||||
|
AuthenticationConverter converterBean = mock(AuthenticationConverter.class);
|
||||||
|
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||||
|
context.registerBean(AuthenticationConverter.class, () -> converterBean);
|
||||||
|
this.spring.context(context).autowire();
|
||||||
|
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
||||||
|
oauth2.authenticationConverter(converter);
|
||||||
|
assertThat(oauth2.getAuthenticationConverter()).isEqualTo(converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenDuplicateConverterBeansThenWiringException() {
|
||||||
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
|
.isThrownBy(
|
||||||
|
() -> this.spring.register(MultipleAuthenticationConverterBeansConfig.class, JwtDecoderConfig.class)
|
||||||
|
.autowire())
|
||||||
|
.withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthenticationConverterWhenNoConverterSpecifiedThenTheDefaultIsUsed() {
|
||||||
|
ApplicationContext context = this.spring.context(new GenericWebApplicationContext()).getContext();
|
||||||
|
OAuth2ResourceServerConfigurer oauth2 = new OAuth2ResourceServerConfigurer(context);
|
||||||
|
assertThat(oauth2.getAuthenticationConverter()).isInstanceOf(BearerTokenAuthenticationConverter.class);
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {
|
private static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {
|
||||||
context.registerBean(name, clazz, () -> mock(clazz));
|
context.registerBean(name, clazz, () -> mock(clazz));
|
||||||
}
|
}
|
||||||
@ -2516,6 +2553,43 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class MultipleAuthenticationConverterBeansConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.oauth2ResourceServer()
|
||||||
|
.jwt();
|
||||||
|
return http.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthenticationConverter authenticationConverterOne() {
|
||||||
|
DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
|
||||||
|
resolver.setAllowUriQueryParameter(true);
|
||||||
|
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
authenticationConverter.setBearerTokenResolver(resolver);
|
||||||
|
return authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthenticationConverter authenticationConverterTwo() {
|
||||||
|
DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
|
||||||
|
resolver.setAllowUriQueryParameter(true);
|
||||||
|
BearerTokenAuthenticationConverter authenticationConverter = new BearerTokenAuthenticationConverter();
|
||||||
|
authenticationConverter.setBearerTokenResolver(resolver);
|
||||||
|
return authenticationConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class MultipleIssuersConfig {
|
static class MultipleIssuersConfig {
|
||||||
@ -2532,7 +2606,9 @@ public class OAuth2ResourceServerConfigurerTests {
|
|||||||
// @formatter:off
|
// @formatter:off
|
||||||
http
|
http
|
||||||
.oauth2ResourceServer()
|
.oauth2ResourceServer()
|
||||||
.authenticationManagerResolver(authenticationManagerResolver);
|
.authenticationManagerResolver(authenticationManagerResolver)
|
||||||
|
.and()
|
||||||
|
.anonymous(AbstractHttpConfigurer::disable);
|
||||||
return http.build();
|
return http.build();
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -25,7 +25,6 @@ import java.time.Instant;
|
|||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -50,13 +49,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
|||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import org.springframework.beans.BeanMetadataElement;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanReference;
|
|
||||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
|
||||||
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||||
import org.springframework.beans.factory.xml.ParserContext;
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||||
@ -85,12 +82,14 @@ import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
|||||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||||
import org.springframework.security.oauth2.jwt.TestJwts;
|
import org.springframework.security.oauth2.jwt.TestJwts;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
|
||||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
|
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
|
||||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
@ -462,6 +461,24 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
|||||||
verify(bearerTokenResolver).resolve(any(HttpServletRequest.class));
|
verify(bearerTokenResolver).resolve(any(HttpServletRequest.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthenticationConverterThenUses() throws Exception {
|
||||||
|
this.spring
|
||||||
|
.configLocations(xml("MockAuthenticationConverter"), xml("MockJwtDecoder"), xml("AuthenticationConverter"))
|
||||||
|
.autowire();
|
||||||
|
JwtDecoder decoder = this.spring.getContext().getBean(JwtDecoder.class);
|
||||||
|
given(decoder.decode("token")).willReturn(TestJwts.jwt().build());
|
||||||
|
AuthenticationConverter authenticationConverter = this.spring.getContext()
|
||||||
|
.getBean(AuthenticationConverter.class);
|
||||||
|
given(authenticationConverter.convert(any(HttpServletRequest.class)))
|
||||||
|
.willReturn(new BearerTokenAuthenticationToken("token"));
|
||||||
|
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isNotFound());
|
||||||
|
|
||||||
|
verify(decoder).decode("token");
|
||||||
|
verify(authenticationConverter).convert(any(HttpServletRequest.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
|
public void requestWhenBearerTokenResolverAllowsRequestBodyThenEitherHeaderOrRequestBodyIsAccepted()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -521,14 +538,6 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getBearerTokenResolverWhenNoResolverSpecifiedThenTheDefaultIsUsed() {
|
|
||||||
OAuth2ResourceServerBeanDefinitionParser oauth2 = new OAuth2ResourceServerBeanDefinitionParser(
|
|
||||||
mock(BeanReference.class), mock(List.class), mock(Map.class), mock(Map.class), mock(List.class),
|
|
||||||
mock(BeanMetadataElement.class));
|
|
||||||
assertThat(oauth2.getBearerTokenResolver(mock(Element.class))).isInstanceOf(RootBeanDefinition.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
|
public void requestWhenCustomJwtDecoderThenUsed() throws Exception {
|
||||||
this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
|
this.spring.configLocations(xml("MockJwtDecoder"), xml("Jwt")).autowire();
|
||||||
@ -545,6 +554,12 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
|
|||||||
.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
|
.isThrownBy(() -> this.spring.configLocations(xml("JwtDecoderAndJwkSetUri")).autowire());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAuthenticationConverterAndJwkSetUriThenException() {
|
||||||
|
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(
|
||||||
|
() -> this.spring.configLocations(xml("AuthenticationConverterAndBearerTokenResolver")).autowire());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
|
public void requestWhenRealmNameConfiguredThenUsesOnUnauthenticated() throws Exception {
|
||||||
this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();
|
this.spring.configLocations(xml("MockJwtDecoder"), xml("AuthenticationEntryPoint")).autowire();
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2002-2020 the original author or authors.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<http>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
<oauth2-resource-server authentication-converter-ref="authenticationConverter">
|
||||||
|
<jwt decoder-ref="decoder"/>
|
||||||
|
</oauth2-resource-server>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<b:import resource="userservice.xml"/>
|
||||||
|
</b:beans>
|
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2002-2020 the original author or authors.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<http>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
<oauth2-resource-server authentication-converter-ref="authenticationConverter" bearer-token-resolver-ref="bearerTokenResolver">
|
||||||
|
<jwt decoder-ref="decoder"/>
|
||||||
|
</oauth2-resource-server>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<b:import resource="userservice.xml"/>
|
||||||
|
</b:beans>
|
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2002-2020 the original author or authors.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://www.springframework.org/schema/security"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||||
|
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<b:bean name="authenticationConverter" class="org.mockito.Mockito" factory-method="mock">
|
||||||
|
<b:constructor-arg value="org.springframework.security.web.authentication.AuthenticationConverter" type="java.lang.Class"/>
|
||||||
|
</b:bean>
|
||||||
|
</b:beans>
|
@ -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,12 +1266,18 @@ 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**
|
||||||
Reference to a `AuthenticationEntryPoint` which will handle unauthorized requests
|
Reference to a `AuthenticationEntryPoint` which will handle unauthorized requests
|
||||||
|
|
||||||
|
[[nsa-oauth2-resource-server-authentication-converter-ref]]
|
||||||
|
* **authentication-converter-ref**
|
||||||
|
Reference to a `AuthenticationConverter` which convert request to authentication.
|
||||||
|
This cannot be used in conjunction with `bearer-token-resolver-ref`
|
||||||
|
|
||||||
[[nsa-jwt]]
|
[[nsa-jwt]]
|
||||||
== <jwt>
|
== <jwt>
|
||||||
Represents an OAuth 2.0 Resource Server that will authorize JWTs
|
Represents an OAuth 2.0 Resource Server that will authorize JWTs
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
com-squareup-okhttp3 = "3.14.9"
|
com-squareup-okhttp3 = "3.14.9"
|
||||||
io-rsocket = "1.1.5"
|
io-rsocket = "1.1.5"
|
||||||
io-spring-javaformat = "0.0.45"
|
io-spring-javaformat = "0.0.46"
|
||||||
io-spring-nohttp = "0.0.11"
|
io-spring-nohttp = "0.0.11"
|
||||||
jakarta-websocket = "2.2.0"
|
jakarta-websocket = "2.2.0"
|
||||||
org-apache-directory-server = "1.5.5"
|
org-apache-directory-server = "1.5.5"
|
||||||
@ -108,7 +108,7 @@ org-jfrog-buildinfo-build-info-extractor-gradle = "org.jfrog.buildinfo:build-inf
|
|||||||
org-sonarsource-scanner-gradle-sonarqube-gradle-plugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8.0.1969"
|
org-sonarsource-scanner-gradle-sonarqube-gradle-plugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8.0.1969"
|
||||||
org-instancio-instancio-junit = "org.instancio:instancio-junit:3.7.1"
|
org-instancio-instancio-junit = "org.instancio:instancio-junit:3.7.1"
|
||||||
|
|
||||||
webauthn4j-core = 'com.webauthn4j:webauthn4j-core:0.29.2.RELEASE'
|
webauthn4j-core = 'com.webauthn4j:webauthn4j-core:0.29.3.RELEASE'
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.oauth2.server.resource.web.authentication;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
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.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link AuthenticationConverter}, that converts request to
|
||||||
|
* {@link BearerTokenAuthenticationToken}
|
||||||
|
*
|
||||||
|
* @author Max Batischev
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public final class BearerTokenAuthenticationConverter implements AuthenticationConverter {
|
||||||
|
|
||||||
|
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
||||||
|
|
||||||
|
private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Authentication convert(HttpServletRequest request) {
|
||||||
|
String token = this.bearerTokenResolver.resolve(request);
|
||||||
|
if (StringUtils.hasText(token)) {
|
||||||
|
BearerTokenAuthenticationToken authenticationToken = new BearerTokenAuthenticationToken(token);
|
||||||
|
authenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||||
|
return authenticationToken;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
|
||||||
|
Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
|
||||||
|
this.bearerTokenResolver = bearerTokenResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link AuthenticationDetailsSource} to use. Defaults to
|
||||||
|
* {@link WebAuthenticationDetailsSource}.
|
||||||
|
* @param authenticationDetailsSource the {@code AuthenticationDetailsSource} to use
|
||||||
|
*/
|
||||||
|
public void setAuthenticationDetailsSource(
|
||||||
|
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
|
||||||
|
Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
|
||||||
|
this.authenticationDetailsSource = authenticationDetailsSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -40,10 +40,12 @@ 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;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
|
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
|
||||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
@ -75,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();
|
||||||
|
|
||||||
@ -83,10 +87,6 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
private AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
|
private AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationEntryPointFailureHandler(
|
||||||
(request, response, exception) -> this.authenticationEntryPoint.commence(request, response, exception));
|
(request, response, exception) -> this.authenticationEntryPoint.commence(request, response, exception));
|
||||||
|
|
||||||
private BearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
|
|
||||||
|
|
||||||
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
|
|
||||||
|
|
||||||
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
|
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,8 +95,7 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
*/
|
*/
|
||||||
public BearerTokenAuthenticationFilter(
|
public BearerTokenAuthenticationFilter(
|
||||||
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
|
AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver) {
|
||||||
Assert.notNull(authenticationManagerResolver, "authenticationManagerResolver cannot be null");
|
this(authenticationManagerResolver, new BearerTokenAuthenticationConverter());
|
||||||
this.authenticationManagerResolver = authenticationManagerResolver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,24 +155,22 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
String token;
|
Authentication authenticationRequest;
|
||||||
try {
|
try {
|
||||||
token = this.bearerTokenResolver.resolve(request);
|
authenticationRequest = this.authenticationConverter.convert(request);
|
||||||
}
|
}
|
||||||
catch (OAuth2AuthenticationException invalid) {
|
catch (OAuth2AuthenticationException invalid) {
|
||||||
this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
|
this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
|
||||||
this.authenticationEntryPoint.commence(request, response, invalid);
|
this.authenticationEntryPoint.commence(request, response, invalid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (token == null) {
|
|
||||||
|
if (authenticationRequest == null) {
|
||||||
this.logger.trace("Did not process request since did not find bearer token");
|
this.logger.trace("Did not process request since did not find bearer token");
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
|
|
||||||
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
|
AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
|
||||||
Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
|
Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
|
||||||
@ -191,10 +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.bearerTokenResolver = bearerTokenResolver;
|
if (this.authenticationConverter instanceof BearerTokenAuthenticationConverter converter) {
|
||||||
|
converter.setBearerTokenResolver(bearerTokenResolver);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"You cannot both specify an AuthenticationConverter and a BearerTokenResolver.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -221,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) {
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2025 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.oauth2.server.resource.web.authentication;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||||
|
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link BearerTokenAuthenticationConverter}
|
||||||
|
*
|
||||||
|
* @author Max Batischev
|
||||||
|
*/
|
||||||
|
public class BearerTokenAuthenticationConverterTests {
|
||||||
|
|
||||||
|
private static final String X_AUTH_TOKEN_HEADER = "X-Auth-Token";
|
||||||
|
|
||||||
|
private static final String TEST_X_AUTH_TOKEN = "test-x-auth-token";
|
||||||
|
|
||||||
|
private static final String BEARER_TOKEN = "test_bearer_token";
|
||||||
|
|
||||||
|
private final DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
|
||||||
|
|
||||||
|
private final BearerTokenAuthenticationConverter converter = new BearerTokenAuthenticationConverter();
|
||||||
|
|
||||||
|
{
|
||||||
|
this.converter.setBearerTokenResolver(this.resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenAuthorizationHeaderIsPresentThenTokenIsConverted() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN);
|
||||||
|
|
||||||
|
Authentication authentication = this.converter.convert(request);
|
||||||
|
|
||||||
|
assertThat(authentication).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenQueryParameterIsPresentThenTokenIsConverted() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.setMethod(HttpMethod.GET.name());
|
||||||
|
request.addParameter("access_token", BEARER_TOKEN);
|
||||||
|
|
||||||
|
this.resolver.setAllowUriQueryParameter(true);
|
||||||
|
|
||||||
|
Authentication authentication = this.converter.convert(request);
|
||||||
|
assertThat(authentication).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenAuthorizationHeaderNotIsPresentThenTokenIsNotConverted() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
|
||||||
|
Authentication authentication = this.converter.convert(request);
|
||||||
|
|
||||||
|
assertThat(authentication).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenAuthorizationHeaderIsPresentTogetherWithQueryParameterThenAuthenticationExceptionIsThrown() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addParameter("access_token", BEARER_TOKEN);
|
||||||
|
request.setMethod(HttpMethod.GET.name());
|
||||||
|
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN);
|
||||||
|
|
||||||
|
this.resolver.setAllowUriQueryParameter(true);
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))
|
||||||
|
.withMessageContaining("Found multiple bearer tokens in the request");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenXAuthTokenHeaderIsPresentAndBearerTokenHeaderNameSetThenTokenIsConverted() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader(X_AUTH_TOKEN_HEADER, "Bearer " + TEST_X_AUTH_TOKEN);
|
||||||
|
|
||||||
|
this.resolver.setBearerTokenHeaderName(X_AUTH_TOKEN_HEADER);
|
||||||
|
|
||||||
|
Authentication authentication = this.converter.convert(request);
|
||||||
|
assertThat(authentication).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenHeaderWithMissingTokenIsPresentThenAuthenticationExceptionIsThrown() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer ");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))
|
||||||
|
.withMessageContaining(("Bearer token is malformed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenHeaderWithInvalidCharactersIsPresentThenAuthenticationExceptionIsThrown() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer an\"invalid\"token");
|
||||||
|
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> this.converter.convert(request))
|
||||||
|
.withMessageContaining(("Bearer token is malformed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void convertWhenCustomAuthenticationDetailsSourceSetThenTokenIsConverted() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + BEARER_TOKEN);
|
||||||
|
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = Mockito
|
||||||
|
.mock(AuthenticationDetailsSource.class);
|
||||||
|
this.converter.setAuthenticationDetailsSource(authenticationDetailsSource);
|
||||||
|
|
||||||
|
Authentication authentication = this.converter.convert(request);
|
||||||
|
|
||||||
|
verify(authenticationDetailsSource).buildDetails(any());
|
||||||
|
assertThat(authentication).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenFormParameterIsPresentAndAllowFormEncodedBodyParameterThenConverted() {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.setMethod(HttpMethod.POST.name());
|
||||||
|
request.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||||
|
request.addParameter("access_token", BEARER_TOKEN);
|
||||||
|
this.resolver.setAllowFormEncodedBodyParameter(true);
|
||||||
|
|
||||||
|
assertThat(this.converter.convert(request)).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -52,6 +52,7 @@ import org.springframework.security.oauth2.server.resource.authentication.Bearer
|
|||||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||||
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
|
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
|
||||||
import org.springframework.security.web.context.SecurityContextRepository;
|
import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
@ -263,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);
|
||||||
@ -293,6 +312,15 @@ public class BearerTokenAuthenticationFilterTests {
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setConverterWhenNullThenThrowsException() {
|
||||||
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenAuthenticationFilter(this.authenticationManager, null))
|
||||||
|
.withMessageContaining("authenticationConverter cannot be null");
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenNullAuthenticationManagerThenThrowsException() {
|
public void constructorWhenNullAuthenticationManagerThenThrowsException() {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -64,6 +64,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
|||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Sergey Bespalov
|
* @author Sergey Bespalov
|
||||||
|
* @author Andrey Litvitski
|
||||||
* @since 5.2.0
|
* @since 5.2.0
|
||||||
*/
|
*/
|
||||||
public class AuthenticationFilter extends OncePerRequestFilter {
|
public class AuthenticationFilter extends OncePerRequestFilter {
|
||||||
@ -193,6 +194,15 @@ public class AuthenticationFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getAlreadyFilteredAttributeName() {
|
||||||
|
String name = getFilterName();
|
||||||
|
if (name == null) {
|
||||||
|
name = getClass().getName().concat("-" + System.identityHashCode(this));
|
||||||
|
}
|
||||||
|
return name + ALREADY_FILTERED_SUFFIX;
|
||||||
|
}
|
||||||
|
|
||||||
private void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
private void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
|
||||||
AuthenticationException failed) throws IOException, ServletException {
|
AuthenticationException failed) throws IOException, ServletException {
|
||||||
this.securityContextHolderStrategy.clearContext();
|
this.securityContextHolderStrategy.clearContext();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,6 +17,7 @@
|
|||||||
package org.springframework.security.web.authentication;
|
package org.springframework.security.web.authentication;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.Servlet;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.ServletRequest;
|
import jakarta.servlet.ServletRequest;
|
||||||
import jakarta.servlet.ServletResponse;
|
import jakarta.servlet.ServletResponse;
|
||||||
@ -57,6 +58,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sergey Bespalov
|
* @author Sergey Bespalov
|
||||||
|
* @author Andrey Litvitski
|
||||||
* @since 5.2.0
|
* @since 5.2.0
|
||||||
*/
|
*/
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@ -318,4 +320,18 @@ public class AuthenticationFilterTests {
|
|||||||
assertThat(securityContextArg.getValue().getAuthentication()).isEqualTo(authentication);
|
assertThat(securityContextArg.getValue().getAuthentication()).isEqualTo(authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterWhenMultipleInChainThenAllFiltered() throws Exception {
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
AuthenticationFilter filter1 = new AuthenticationFilter(this.authenticationManager,
|
||||||
|
this.authenticationConverter);
|
||||||
|
AuthenticationConverter converter2 = mock(AuthenticationConverter.class);
|
||||||
|
AuthenticationFilter filter2 = new AuthenticationFilter(this.authenticationManager, converter2);
|
||||||
|
FilterChain chain = new MockFilterChain(mock(Servlet.class), filter1, filter2);
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
verify(this.authenticationConverter).convert(any());
|
||||||
|
verify(converter2).convert(any());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user