1
0
mirror of synced 2026-08-06 02:08:01 +00:00

Polish gh-11665

This commit is contained in:
Steve Riesenberg
2022-09-06 20:28:10 -05:00
parent 1efb63387f
commit 355ef21117
14 changed files with 258 additions and 88 deletions
@@ -21,12 +21,10 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.MediaType;
@@ -460,7 +458,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
private Supplier<OpaqueTokenIntrospector> introspector;
private Supplier<OpaqueTokenAuthenticationConverter> authenticationConverter;
private OpaqueTokenAuthenticationConverter authenticationConverter;
OpaqueTokenConfigurer(ApplicationContext context) {
this.context = context;
@@ -499,7 +497,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
public OpaqueTokenConfigurer authenticationConverter(
OpaqueTokenAuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = () -> authenticationConverter;
this.authenticationConverter = authenticationConverter;
return this;
}
@@ -510,16 +508,14 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this.context.getBean(OpaqueTokenIntrospector.class);
}
Optional<OpaqueTokenAuthenticationConverter> getAuthenticationConverter() {
OpaqueTokenAuthenticationConverter getAuthenticationConverter() {
if (this.authenticationConverter != null) {
return Optional.of(this.authenticationConverter.get());
return this.authenticationConverter;
}
try {
return Optional.of(this.context.getBean(OpaqueTokenAuthenticationConverter.class));
}
catch (NoSuchBeanDefinitionException nsbde) {
return Optional.empty();
if (this.context.getBeanNamesForType(OpaqueTokenAuthenticationConverter.class).length > 0) {
return this.context.getBean(OpaqueTokenAuthenticationConverter.class);
}
return null;
}
AuthenticationProvider getAuthenticationProvider() {
@@ -527,9 +523,12 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return null;
}
OpaqueTokenIntrospector introspector = getIntrospector();
final OpaqueTokenAuthenticationProvider opaqueTokenAuthenticationProvider = new OpaqueTokenAuthenticationProvider(
OpaqueTokenAuthenticationProvider opaqueTokenAuthenticationProvider = new OpaqueTokenAuthenticationProvider(
introspector);
getAuthenticationConverter().ifPresent(opaqueTokenAuthenticationProvider::setAuthenticationConverter);
OpaqueTokenAuthenticationConverter authenticationConverter = getAuthenticationConverter();
if (authenticationConverter != null) {
opaqueTokenAuthenticationProvider.setAuthenticationConverter(authenticationConverter);
}
return opaqueTokenAuthenticationProvider;
}
@@ -252,6 +252,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
static final String CLIENT_SECRET = "client-secret";
static final String AUTHENTICATION_CONVERTER_REF = "authentication-converter-ref";
static final String AUTHENTICATION_CONVERTER = "authenticationConverter";
OpaqueTokenBeanDefinitionParser() {
@@ -266,8 +267,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa
.rootBeanDefinition(OpaqueTokenAuthenticationProvider.class);
opaqueTokenProviderBuilder.addConstructorArgValue(introspector);
if (StringUtils.hasText(authenticationConverterRef)) {
opaqueTokenProviderBuilder.addPropertyValue(AUTHENTICATION_CONVERTER,
new RuntimeBeanReference(authenticationConverterRef));
opaqueTokenProviderBuilder.addPropertyReference(AUTHENTICATION_CONVERTER, authenticationConverterRef);
}
return opaqueTokenProviderBuilder.getBeanDefinition();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
@@ -27,7 +27,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -36,7 +35,6 @@ import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
@@ -4286,7 +4284,7 @@ public class ServerHttpSecurity {
private Supplier<ReactiveOpaqueTokenIntrospector> introspector;
private Supplier<ReactiveOpaqueTokenAuthenticationConverter> authenticationConverter;
private ReactiveOpaqueTokenAuthenticationConverter authenticationConverter;
private OpaqueTokenSpec() {
}
@@ -4329,7 +4327,7 @@ public class ServerHttpSecurity {
public OpaqueTokenSpec authenticationConverter(
ReactiveOpaqueTokenAuthenticationConverter authenticationConverter) {
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
this.authenticationConverter = () -> authenticationConverter;
this.authenticationConverter = authenticationConverter;
return this;
}
@@ -4343,10 +4341,12 @@ public class ServerHttpSecurity {
}
protected ReactiveAuthenticationManager getAuthenticationManager() {
final OpaqueTokenReactiveAuthenticationManager authenticationManager = new OpaqueTokenReactiveAuthenticationManager(
OpaqueTokenReactiveAuthenticationManager authenticationManager = new OpaqueTokenReactiveAuthenticationManager(
getIntrospector());
Optional.ofNullable(getAuthenticationConverter())
.ifPresent(authenticationManager::setAuthenticationConverter);
ReactiveOpaqueTokenAuthenticationConverter authenticationConverter = getAuthenticationConverter();
if (authenticationConverter != null) {
authenticationManager.setAuthenticationConverter(authenticationConverter);
}
return authenticationManager;
}
@@ -4359,14 +4359,9 @@ public class ServerHttpSecurity {
protected ReactiveOpaqueTokenAuthenticationConverter getAuthenticationConverter() {
if (this.authenticationConverter != null) {
return this.authenticationConverter.get();
}
try {
return getBean(ReactiveOpaqueTokenAuthenticationConverter.class);
}
catch (NoSuchBeanDefinitionException nsbde) {
return null;
return this.authenticationConverter;
}
return getBeanOrNull(ReactiveOpaqueTokenAuthenticationConverter.class);
}
protected void configure(ServerHttpSecurity http) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 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.
@@ -31,7 +31,6 @@ import org.springframework.security.oauth2.server.resource.introspection.Reactiv
class ServerOpaqueTokenDsl {
private var _introspectionUri: String? = null
private var _introspector: ReactiveOpaqueTokenIntrospector? = null
private var _authenticationConverter: ReactiveOpaqueTokenAuthenticationConverter? = null
private var clientCredentials: Pair<String, String>? = null
var introspectionUri: String?
@@ -39,21 +38,15 @@ class ServerOpaqueTokenDsl {
set(value) {
_introspectionUri = value
_introspector = null
_authenticationConverter = null
}
var introspector: ReactiveOpaqueTokenIntrospector?
get() = _introspector
set(value) {
_introspector = value
_authenticationConverter = null
_introspectionUri = null
clientCredentials = null
}
var authenticationConverter: ReactiveOpaqueTokenAuthenticationConverter?
get() = _authenticationConverter
set(value) {
_authenticationConverter = value
}
var authenticationConverter: ReactiveOpaqueTokenAuthenticationConverter? = null
/**
* Configures the credentials for Introspection endpoint.
@@ -64,7 +57,6 @@ class ServerOpaqueTokenDsl {
fun introspectionClientCredentials(clientId: String, clientSecret: String) {
clientCredentials = Pair(clientId, clientSecret)
_introspector = null
_authenticationConverter = null
}
internal fun get(): (ServerHttpSecurity.OAuth2ResourceServerSpec.OpaqueTokenSpec) -> Unit {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
@@ -38,7 +38,6 @@ class OpaqueTokenDsl {
private var _introspectionUri: String? = null
private var _introspector: OpaqueTokenIntrospector? = null
private var clientCredentials: Pair<String, String>? = null
private var _authenticationConverter: OpaqueTokenAuthenticationConverter? = null
var authenticationManager: AuthenticationManager? = null
@@ -56,11 +55,7 @@ class OpaqueTokenDsl {
clientCredentials = null
}
var authenticationConverter: OpaqueTokenAuthenticationConverter?
get() = _authenticationConverter
set(value) {
_authenticationConverter = value
}
var authenticationConverter: OpaqueTokenAuthenticationConverter? = null
/**
* Configures the credentials for Introspection endpoint.
@@ -82,6 +82,7 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@@ -103,6 +104,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
@@ -121,6 +123,7 @@ import org.springframework.security.oauth2.server.resource.authentication.JwtAut
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
@@ -1387,6 +1390,22 @@ public class OAuth2ResourceServerConfigurerTests {
.isThrownBy(jwtConfigurer::getJwtAuthenticationConverter);
}
@Test
public void getWhenCustomAuthenticationConverterThenConverts() throws Exception {
this.spring.register(RestOperationsConfig.class, OpaqueTokenAuthenticationConverterConfig.class,
BasicController.class).autowire();
OpaqueTokenAuthenticationConverter authenticationConverter = this.spring.getContext()
.getBean(OpaqueTokenAuthenticationConverter.class);
given(authenticationConverter.convert(anyString(), any(OAuth2AuthenticatedPrincipal.class)))
.willReturn(new TestingAuthenticationToken("jdoe", null, Collections.emptyList()));
mockRestOperations(json("Active"));
// @formatter:off
this.mvc.perform(get("/authenticated").with(bearerToken("token")))
.andExpect(status().isOk())
.andExpect(content().string("jdoe"));
// @formatter:on
}
private static <T> void registerMockBean(GenericApplicationContext context, String name, Class<T> clazz) {
context.registerBean(name, clazz, () -> mock(clazz));
}
@@ -2441,6 +2460,30 @@ public class OAuth2ResourceServerConfigurerTests {
}
@EnableWebSecurity
static class OpaqueTokenAuthenticationConverterConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.antMatchers("/requires-read-scope").hasAuthority("SCOPE_message:read")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.opaqueToken()
.authenticationConverter(authenticationConverter());
// @formatter:on
}
@Bean
OpaqueTokenAuthenticationConverter authenticationConverter() {
return mock(OpaqueTokenAuthenticationConverter.class);
}
}
@Configuration
static class JwtDecoderConfig {
@@ -24,6 +24,7 @@ import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
import java.util.Collections;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -52,11 +53,13 @@ import org.springframework.http.MediaType;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.jwt.Jwt;
@@ -66,6 +69,7 @@ import org.springframework.security.oauth2.server.resource.BearerTokenAuthentica
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
@@ -567,6 +571,25 @@ public class OAuth2ResourceServerSpecTests {
.withMessageContaining("authenticationManagerResolver");
}
@Test
public void getWhenCustomAuthenticationConverterThenConverts() {
this.spring.register(ReactiveOpaqueTokenAuthenticationConverterConfig.class, RootController.class).autowire();
this.spring.getContext().getBean(MockWebServer.class)
.setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active));
ReactiveOpaqueTokenAuthenticationConverter authenticationConverter = this.spring.getContext()
.getBean(ReactiveOpaqueTokenAuthenticationConverter.class);
given(authenticationConverter.convert(anyString(), any(OAuth2AuthenticatedPrincipal.class)))
.willReturn(Mono.just(new TestingAuthenticationToken("jdoe", null, Collections.emptyList())));
// @formatter:off
this.client.get()
.headers((headers) -> headers
.setBearerAuth(this.messageReadToken)
)
.exchange()
.expectStatus().isOk();
// @formatter:on
}
private static Dispatcher requiresAuth(String username, String password, String response) {
return new Dispatcher() {
@Override
@@ -1037,6 +1060,43 @@ public class OAuth2ResourceServerSpecTests {
}
@EnableWebFlux
@EnableWebFluxSecurity
static class ReactiveOpaqueTokenAuthenticationConverterConfig {
private MockWebServer mockWebServer = new MockWebServer();
@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
String introspectionUri = mockWebServer().url("/introspect").toString();
// @formatter:off
http
.oauth2ResourceServer()
.opaqueToken()
.introspectionUri(introspectionUri)
.introspectionClientCredentials("client", "secret")
.authenticationConverter(authenticationConverter());
// @formatter:on
return http.build();
}
@Bean
ReactiveOpaqueTokenAuthenticationConverter authenticationConverter() {
return mock(ReactiveOpaqueTokenAuthenticationConverter.class);
}
@Bean
MockWebServer mockWebServer() {
return this.mockWebServer;
}
@PreDestroy
void shutdown() throws IOException {
this.mockWebServer.shutdown();
}
}
@RestController
static class RootController {