Browse Source

Support both JWT and Opaque token configuration for resource server

Closes gh-19426
pull/20830/head
Madhura Bhave 6 years ago
parent
commit
c2e95ee7ee
  1. 24
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java
  2. 23
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java
  3. 50
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerConfiguration.java
  4. 26
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfiguration.java
  5. 52
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/Oauth2ResourceServerConfiguration.java
  6. 44
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java
  7. 47
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java

24
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -19,8 +19,6 @@ import java.io.IOException; @@ -19,8 +19,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import javax.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.core.io.Resource;
@ -49,26 +47,6 @@ public class OAuth2ResourceServerProperties { @@ -49,26 +47,6 @@ public class OAuth2ResourceServerProperties {
return this.opaqueToken;
}
@PostConstruct
public void validate() {
if (this.getOpaquetoken().getIntrospectionUri() != null) {
if (this.getJwt().getJwkSetUri() != null) {
handleError("jwt.jwk-set-uri");
}
if (this.getJwt().getIssuerUri() != null) {
handleError("jwt.issuer-uri");
}
if (this.getJwt().getPublicKeyLocation() != null) {
handleError("jwt.public-key-location");
}
}
}
private void handleError(String property) {
throw new IllegalStateException(
"Only one of " + property + " and opaquetoken.introspection-uri should be configured.");
}
public static class Jwt {
/**

23
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -26,9 +26,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties @@ -26,9 +26,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Reactive OAuth2 resource server
@ -42,22 +39,8 @@ import org.springframework.security.oauth2.server.resource.introspection.Reactiv @@ -42,22 +39,8 @@ import org.springframework.security.oauth2.server.resource.introspection.Reactiv
@EnableConfigurationProperties(OAuth2ResourceServerProperties.class)
@ConditionalOnClass({ EnableWebFluxSecurity.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@Import({ ReactiveOAuth2ResourceServerConfiguration.JwtConfiguration.class,
ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenConfiguration.class })
public class ReactiveOAuth2ResourceServerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class })
@Import({ ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class,
ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class })
static class JwtConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class })
@Import({ ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class })
static class OpaqueTokenConfiguration {
}
}

50
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerConfiguration.java

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
/*
* Copyright 2012-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.
*/
package org.springframework.boot.autoconfigure.security.oauth2.resource.reactive;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
/**
* Configuration classes for OAuth2 Resource Server These should be {@code @Import} in a
* regular auto-configuration class to guarantee their order of execution.
*
* @author Madhura Bhave
*/
class ReactiveOAuth2ResourceServerConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class })
@Import({ ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class,
ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class })
static class JwtConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class })
@Import({ ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class })
static class OpaqueTokenConfiguration {
}
}

26
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-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.
@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet; @@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
@ -25,10 +24,6 @@ import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServic @@ -25,10 +24,6 @@ import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServic
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
/**
* {@link EnableAutoConfiguration Auto-configuration} for OAuth2 resource server support.
@ -40,23 +35,8 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT @@ -40,23 +35,8 @@ import org.springframework.security.oauth2.server.resource.introspection.OpaqueT
@AutoConfigureBefore({ SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class })
@EnableConfigurationProperties(OAuth2ResourceServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@Import({ Oauth2ResourceServerConfiguration.JwtConfiguration.class,
Oauth2ResourceServerConfiguration.OpaqueTokenConfiguration.class })
public class OAuth2ResourceServerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JwtAuthenticationToken.class, JwtDecoder.class })
@Import({ OAuth2ResourceServerJwtConfiguration.JwtDecoderConfiguration.class,
OAuth2ResourceServerJwtConfiguration.OAuth2WebSecurityConfigurerAdapter.class })
static class JwtConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, OpaqueTokenIntrospector.class })
@Import({ OAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
OAuth2ResourceServerOpaqueTokenConfiguration.OAuth2WebSecurityConfigurerAdapter.class })
static class OpaqueTokenConfiguration {
}
}

52
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/Oauth2ResourceServerConfiguration.java

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
/*
* Copyright 2012-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.
*/
package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
/**
* Configuration classes for OAuth2 Resource Server
* These should be {@code @Import} in a regular auto-configuration class to guarantee
* their order of execution.
*
* @author Madhura Bhave
*/
class Oauth2ResourceServerConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JwtAuthenticationToken.class, JwtDecoder.class })
@Import({ OAuth2ResourceServerJwtConfiguration.JwtDecoderConfiguration.class,
OAuth2ResourceServerJwtConfiguration.OAuth2WebSecurityConfigurerAdapter.class })
static class JwtConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, OpaqueTokenIntrospector.class })
@Import({ OAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
OAuth2ResourceServerOpaqueTokenConfiguration.OAuth2WebSecurityConfigurerAdapter.class })
static class OpaqueTokenConfiguration {
}
}

44
spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

@ -288,6 +288,20 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { @@ -288,6 +288,20 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
});
}
@Test
void autoConfigurationWhenJwkSetUriAndIntrospectionUriAvailable() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com",
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id",
"spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret")
.run((context) -> {
assertThat(context).hasSingleBean(ReactiveOpaqueTokenIntrospector.class);
assertThat(context).hasSingleBean(ReactiveJwtDecoder.class);
assertFilterConfiguredWithJwtAuthenticationManager(context);
});
}
@Test
void opaqueTokenIntrospectorIsConditionalOnMissingBean() {
this.contextRunner
@ -320,36 +334,6 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { @@ -320,36 +334,6 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
.run((context) -> assertThat(context).doesNotHaveBean(ReactiveOpaqueTokenIntrospector.class));
}
@Test
void autoConfigurationWhenBothJwkSetUriAndTokenIntrospectionUriSetShouldFail() {
this.contextRunner
.withPropertyValues(
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com")
.run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining(
"Only one of jwt.jwk-set-uri and opaquetoken.introspection-uri should be configured."));
}
@Test
void autoConfigurationWhenBothJwtIssuerUriAndTokenIntrospectionUriSetShouldFail() {
this.contextRunner
.withPropertyValues(
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.jwt.issuer-uri=https://jwk-oidc-issuer-location.com")
.run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining(
"Only one of jwt.issuer-uri and opaquetoken.introspection-uri should be configured."));
}
@Test
void autoConfigurationWhenBothJwtKeyLocationAndTokenIntrospectionUriSetShouldFail() {
this.contextRunner
.withPropertyValues(
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.jwt.public-key-location=classpath:public-key-location")
.run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining(
"Only one of jwt.public-key-location and opaquetoken.introspection-uri should be configured."));
}
@SuppressWarnings("unchecked")
@Test
void autoConfigurationShouldConfigureResourceServerUsingJwkSetUriAndIssuerUri() throws Exception {

47
spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java

@ -48,6 +48,7 @@ import org.springframework.security.oauth2.jwt.Jwt; @@ -48,6 +48,7 @@ import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtIssuerValidator;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
@ -273,6 +274,22 @@ class OAuth2ResourceServerAutoConfigurationTests { @@ -273,6 +274,22 @@ class OAuth2ResourceServerAutoConfigurationTests {
.run((context) -> assertThat(getBearerTokenFilter(context)).isNull());
}
@Test
void autoConfigurationWhenJwkSetUriAndIntrospectionUriAvailable() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com",
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id",
"spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret")
.run((context) -> {
assertThat(context).hasSingleBean(OpaqueTokenIntrospector.class);
assertThat(context).hasSingleBean(JwtDecoder.class);
assertThat(getBearerTokenFilter(context))
.extracting("authenticationManagerResolver.arg$1.providers").asList()
.hasAtLeastOneElementOfType(JwtAuthenticationProvider.class);
});
}
@Test
void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionClient() {
this.contextRunner
@ -305,36 +322,6 @@ class OAuth2ResourceServerAutoConfigurationTests { @@ -305,36 +322,6 @@ class OAuth2ResourceServerAutoConfigurationTests {
.run((context) -> assertThat(context).doesNotHaveBean(OpaqueTokenIntrospector.class));
}
@Test
void autoConfigurationWhenBothJwkSetUriAndTokenIntrospectionUriSetShouldFail() {
this.contextRunner
.withPropertyValues(
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com")
.run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining(
"Only one of jwt.jwk-set-uri and opaquetoken.introspection-uri should be configured."));
}
@Test
void autoConfigurationWhenBothJwtIssuerUriAndTokenIntrospectionUriSetShouldFail() {
this.contextRunner
.withPropertyValues(
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.jwt.issuer-uri=https://jwk-oidc-issuer-location.com")
.run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining(
"Only one of jwt.issuer-uri and opaquetoken.introspection-uri should be configured."));
}
@Test
void autoConfigurationWhenBothJwtKeyLocationAndTokenIntrospectionUriSetShouldFail() {
this.contextRunner
.withPropertyValues(
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.jwt.public-key-location=classpath:public-key-location")
.run((context) -> assertThat(context).hasFailed().getFailure().hasMessageContaining(
"Only one of jwt.public-key-location and opaquetoken.introspection-uri should be configured."));
}
@SuppressWarnings("unchecked")
@Test
void autoConfigurationShouldConfigureResourceServerUsingJwkSetUriAndIssuerUri() throws Exception {

Loading…
Cancel
Save