Add configuration support for Opaque Token authentication
Closes gh-15872
This commit is contained in:
parent
8d44e31898
commit
2560b54f7c
|
@ -19,6 +19,8 @@ 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;
|
||||
|
@ -41,6 +43,32 @@ public class OAuth2ResourceServerProperties {
|
|||
return this.jwt;
|
||||
}
|
||||
|
||||
private final OpaqueToken opaqueToken = new OpaqueToken();
|
||||
|
||||
public OpaqueToken getOpaqueToken() {
|
||||
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 opaque-token.introspection-uri should be configured.");
|
||||
}
|
||||
|
||||
public static class Jwt {
|
||||
|
||||
/**
|
||||
|
@ -109,4 +137,47 @@ public class OAuth2ResourceServerProperties {
|
|||
|
||||
}
|
||||
|
||||
public static class OpaqueToken {
|
||||
|
||||
/**
|
||||
* Client id used to authenticate with the token introspection endpoint.
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* Client secret used to authenticate with the token introspection endpoint.
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* OAuth 2.0 endpoint through which token introspection is accomplished.
|
||||
*/
|
||||
private String introspectionUri;
|
||||
|
||||
public String getClientId() {
|
||||
return this.clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return this.clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getIntrospectionUri() {
|
||||
return this.introspectionUri;
|
||||
}
|
||||
|
||||
public void setIntrospectionUri(String introspectionUri) {
|
||||
this.introspectionUri = introspectionUri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ 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.authentication.OAuth2IntrospectionAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOAuth2TokenIntrospectionClient;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Reactive OAuth2 resource server
|
||||
|
@ -38,10 +40,24 @@ import org.springframework.security.oauth2.server.resource.BearerTokenAuthentica
|
|||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureBefore(ReactiveSecurityAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(OAuth2ResourceServerProperties.class)
|
||||
@ConditionalOnClass({ EnableWebFluxSecurity.class, BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class })
|
||||
@ConditionalOnClass({ EnableWebFluxSecurity.class })
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@Import({ ReactiveOAuth2ResourceServerJwkConfiguration.class,
|
||||
ReactiveOAuth2ResourceServerWebSecurityConfiguration.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({ OAuth2IntrospectionAuthenticationToken.class, ReactiveOAuth2TokenIntrospectionClient.class })
|
||||
@Import({ ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
|
||||
ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class })
|
||||
static class OpaqueTokenConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.security.interfaces.RSAPublicKey;
|
|||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.IssuerUriCondition;
|
||||
|
@ -28,13 +29,16 @@ import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2Res
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
/**
|
||||
* Configures a {@link ReactiveJwtDecoder} when a JWK Set URI, OpenID Connect Issuer URI
|
||||
* or Public Key configuration is available.
|
||||
* or Public Key configuration is available. Also configures a
|
||||
* {@link SecurityWebFilterChain} if a {@link ReactiveJwtDecoder} bean is found.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Artsiom Yudovin
|
||||
|
@ -42,38 +46,56 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
|
|||
@Configuration(proxyBeanMethods = false)
|
||||
class ReactiveOAuth2ResourceServerJwkConfiguration {
|
||||
|
||||
private final OAuth2ResourceServerProperties.Jwt properties;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(ReactiveJwtDecoder.class)
|
||||
static class JwtConfiguration {
|
||||
|
||||
private final OAuth2ResourceServerProperties.Jwt properties;
|
||||
|
||||
JwtConfiguration(OAuth2ResourceServerProperties properties) {
|
||||
this.properties = properties.getJwt();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
|
||||
public ReactiveJwtDecoder jwtDecoder() {
|
||||
return new NimbusReactiveJwtDecoder(this.properties.getJwkSetUri());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(KeyValueCondition.class)
|
||||
public NimbusReactiveJwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
|
||||
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
|
||||
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
|
||||
return NimbusReactiveJwtDecoder.withPublicKey(publicKey).build();
|
||||
}
|
||||
|
||||
private byte[] getKeySpec(String keyValue) {
|
||||
keyValue = keyValue.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
|
||||
return Base64.getMimeDecoder().decode(keyValue);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
public ReactiveJwtDecoder jwtDecoderByIssuerUri() {
|
||||
return ReactiveJwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
|
||||
}
|
||||
|
||||
ReactiveOAuth2ResourceServerJwkConfiguration(OAuth2ResourceServerProperties properties) {
|
||||
this.properties = properties.getJwt();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
|
||||
@ConditionalOnMissingBean
|
||||
public ReactiveJwtDecoder jwtDecoder() {
|
||||
return new NimbusReactiveJwtDecoder(this.properties.getJwkSetUri());
|
||||
}
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(SecurityWebFilterChain.class)
|
||||
static class WebSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
@Conditional(KeyValueCondition.class)
|
||||
@ConditionalOnMissingBean
|
||||
public NimbusReactiveJwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
|
||||
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
|
||||
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
|
||||
return NimbusReactiveJwtDecoder.withPublicKey(publicKey).build();
|
||||
}
|
||||
@Bean
|
||||
@ConditionalOnBean(ReactiveJwtDecoder.class)
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
|
||||
ReactiveJwtDecoder jwtDecoder) {
|
||||
http.authorizeExchange().anyExchange().authenticated().and().oauth2ResourceServer().jwt()
|
||||
.jwtDecoder(jwtDecoder);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
private byte[] getKeySpec(String keyValue) {
|
||||
keyValue = keyValue.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
|
||||
return Base64.getMimeDecoder().decode(keyValue);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
@ConditionalOnMissingBean
|
||||
public ReactiveJwtDecoder jwtDecoderByIssuerUri() {
|
||||
return ReactiveJwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOAuth2TokenIntrospectionClient;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOAuth2TokenIntrospectionClient;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
/**
|
||||
* Configures a {@link ReactiveOAuth2TokenIntrospectionClient} when a token introspection
|
||||
* endpoint is available. Also configures a {@link SecurityWebFilterChain} if a
|
||||
* {@link ReactiveOAuth2TokenIntrospectionClient} bean is found.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class ReactiveOAuth2ResourceServerOpaqueTokenConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(ReactiveOAuth2TokenIntrospectionClient.class)
|
||||
static class OpaqueTokenIntrospectionClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.opaque-token.introspection-uri")
|
||||
public NimbusReactiveOAuth2TokenIntrospectionClient oAuth2TokenIntrospectionClient(
|
||||
OAuth2ResourceServerProperties properties) {
|
||||
OAuth2ResourceServerProperties.OpaqueToken opaqueToken = properties.getOpaqueToken();
|
||||
return new NimbusReactiveOAuth2TokenIntrospectionClient(opaqueToken.getIntrospectionUri(),
|
||||
opaqueToken.getClientId(), opaqueToken.getClientSecret());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(SecurityWebFilterChain.class)
|
||||
static class WebSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ReactiveOAuth2TokenIntrospectionClient.class)
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange().anyExchange().authenticated().and().oauth2ResourceServer().opaqueToken();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
/**
|
||||
* Configures a {@link SecurityWebFilterChain} for Reactive OAuth2 resource server support
|
||||
* if a {@link ReactiveJwtDecoder} bean is present.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(ReactiveJwtDecoder.class)
|
||||
class ReactiveOAuth2ResourceServerWebSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveJwtDecoder jwtDecoder) {
|
||||
http.authorizeExchange().anyExchange().authenticated().and().oauth2ResourceServer().jwt()
|
||||
.jwtDecoder(jwtDecoder);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -26,9 +26,11 @@ 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.authentication.JwtAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OAuth resource server support.
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for OAuth2 resource server support.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.1.0
|
||||
|
@ -36,9 +38,24 @@ import org.springframework.security.oauth2.server.resource.authentication.JwtAut
|
|||
@Configuration(proxyBeanMethods = false)
|
||||
@AutoConfigureBefore(SecurityAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(OAuth2ResourceServerProperties.class)
|
||||
@ConditionalOnClass({ JwtAuthenticationToken.class, JwtDecoder.class })
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@Import({ OAuth2ResourceServerJwtConfiguration.class, OAuth2ResourceServerWebSecurityConfiguration.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({ OAuth2IntrospectionAuthenticationToken.class, OAuth2TokenIntrospectionClient.class })
|
||||
@Import({ OAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
|
||||
OAuth2ResourceServerOpaqueTokenConfiguration.OAuth2WebSecurityConfigurerAdapter.class })
|
||||
static class OpaqueTokenConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.security.interfaces.RSAPublicKey;
|
|||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.IssuerUriCondition;
|
||||
|
@ -28,6 +29,8 @@ import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2Res
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoders;
|
||||
|
@ -35,7 +38,8 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
|||
|
||||
/**
|
||||
* Configures a {@link JwtDecoder} when a JWK Set URI, OpenID Connect Issuer URI or Public
|
||||
* Key configuration is available.
|
||||
* Key configuration is available. Also configures a {@link WebSecurityConfigurerAdapter}
|
||||
* if a {@link JwtDecoder} bean is found.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Artsiom Yudovin
|
||||
|
@ -43,39 +47,59 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
|||
@Configuration(proxyBeanMethods = false)
|
||||
class OAuth2ResourceServerJwtConfiguration {
|
||||
|
||||
private final OAuth2ResourceServerProperties.Jwt properties;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(JwtDecoder.class)
|
||||
static class JwtDecoderConfiguration {
|
||||
|
||||
private final OAuth2ResourceServerProperties.Jwt properties;
|
||||
|
||||
JwtDecoderConfiguration(OAuth2ResourceServerProperties properties) {
|
||||
this.properties = properties.getJwt();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
|
||||
public JwtDecoder jwtDecoderByJwkKeySetUri() {
|
||||
return NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
|
||||
.jwsAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(KeyValueCondition.class)
|
||||
public JwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
|
||||
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
|
||||
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
|
||||
return NimbusJwtDecoder.withPublicKey(publicKey).build();
|
||||
}
|
||||
|
||||
private byte[] getKeySpec(String keyValue) {
|
||||
keyValue = keyValue.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
|
||||
return Base64.getMimeDecoder().decode(keyValue);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
public JwtDecoder jwtDecoderByIssuerUri() {
|
||||
return JwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
|
||||
}
|
||||
|
||||
OAuth2ResourceServerJwtConfiguration(OAuth2ResourceServerProperties properties) {
|
||||
this.properties = properties.getJwt();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
|
||||
@ConditionalOnMissingBean
|
||||
public JwtDecoder jwtDecoderByJwkKeySetUri() {
|
||||
return NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
|
||||
.jwsAlgorithm(SignatureAlgorithm.from(this.properties.getJwsAlgorithm())).build();
|
||||
}
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
|
||||
static class OAuth2WebSecurityConfigurerAdapter {
|
||||
|
||||
@Bean
|
||||
@Conditional(KeyValueCondition.class)
|
||||
@ConditionalOnMissingBean
|
||||
public JwtDecoder jwtDecoderByPublicKeyValue() throws Exception {
|
||||
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
|
||||
.generatePublic(new X509EncodedKeySpec(getKeySpec(this.properties.readPublicKey())));
|
||||
return NimbusJwtDecoder.withPublicKey(publicKey).build();
|
||||
}
|
||||
@Bean
|
||||
@ConditionalOnBean(JwtDecoder.class)
|
||||
public WebSecurityConfigurerAdapter jwtDecoderWebSecurityConfigurerAdapter() {
|
||||
return new WebSecurityConfigurerAdapter() {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().authenticated().and().oauth2ResourceServer().jwt();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] getKeySpec(String keyValue) {
|
||||
keyValue = keyValue.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
|
||||
return Base64.getMimeDecoder().decode(keyValue);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
@ConditionalOnMissingBean
|
||||
public JwtDecoder jwtDecoderByIssuerUri() {
|
||||
return JwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOAuth2TokenIntrospectionClient;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient;
|
||||
|
||||
/**
|
||||
* Configures a {@link OAuth2TokenIntrospectionClient} when a token introspection endpoint
|
||||
* is available. Also configures a {@link WebSecurityConfigurerAdapter} if a
|
||||
* {@link OAuth2TokenIntrospectionClient} bean is found.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class OAuth2ResourceServerOpaqueTokenConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(OAuth2TokenIntrospectionClient.class)
|
||||
static class OpaqueTokenIntrospectionClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.opaque-token.introspection-uri")
|
||||
public NimbusOAuth2TokenIntrospectionClient oAuth2TokenIntrospectionClient(
|
||||
OAuth2ResourceServerProperties properties) {
|
||||
OAuth2ResourceServerProperties.OpaqueToken opaqueToken = properties.getOpaqueToken();
|
||||
return new NimbusOAuth2TokenIntrospectionClient(opaqueToken.getIntrospectionUri(),
|
||||
opaqueToken.getClientId(), opaqueToken.getClientSecret());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
|
||||
static class OAuth2WebSecurityConfigurerAdapter {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(OAuth2TokenIntrospectionClient.class)
|
||||
public WebSecurityConfigurerAdapter opaqueTokenWebSecurityConfigurerAdapter() {
|
||||
return new WebSecurityConfigurerAdapter() {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().authenticated().and().oauth2ResourceServer().opaqueToken();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2019 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.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
|
||||
/**
|
||||
* {@link WebSecurityConfigurerAdapter} for OAuth2 resource server support.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
|
||||
class OAuth2ResourceServerWebSecurityConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(JwtDecoder.class)
|
||||
static class OAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().authenticated().and().oauth2ResourceServer().jwt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -46,6 +46,10 @@ import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
|||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionReactiveAuthenticationManager;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOAuth2TokenIntrospectionClient;
|
||||
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
||||
|
@ -204,6 +208,81 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionClient() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://check-token.com",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-id=my-client-id",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-secret=my-client-secret")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(ReactiveOAuth2TokenIntrospectionClient.class);
|
||||
assertFilterConfiguredWithOpaqueTokenAuthenticationManager(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void oAuth2TokenIntrospectionClientIsConditionalOnMissingBean() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://check-token.com")
|
||||
.withUserConfiguration(OAuth2TokenIntrospectionClientConfig.class)
|
||||
.run((this::assertFilterConfiguredWithOpaqueTokenAuthenticationManager));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationForOpaqueTokenWhenSecurityWebFilterChainConfigPresentShouldNotAddOne() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://check-token.com",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-id=my-client-id",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-secret=my-client-secret")
|
||||
.withUserConfiguration(SecurityWebFilterChainConfig.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(SecurityWebFilterChain.class);
|
||||
assertThat(context).hasBean("testSpringSecurityFilterChain");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenIntrospectionUriAvailableShouldBeConditionalOnClass() {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(OAuth2IntrospectionAuthenticationToken.class))
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://check-token.com",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-id=my-client-id",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-secret=my-client-secret")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(OAuth2TokenIntrospectionClient.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenBothJwkSetUriAndTokenIntrospectionUriSetShouldFail() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.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 opaque-token.introspection-uri should be configured."));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenBothJwtIssuerUriAndTokenIntrospectionUriSetShouldFail() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.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 opaque-token.introspection-uri should be configured."));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenBothJwtKeyLocationAndTokenIntrospectionUriSetShouldFail() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.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 opaque-token.introspection-uri should be configured."));
|
||||
}
|
||||
|
||||
private void assertFilterConfiguredWithJwtAuthenticationManager(AssertableReactiveWebApplicationContext context) {
|
||||
MatcherSecurityWebFilterChain filterChain = (MatcherSecurityWebFilterChain) context
|
||||
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||
|
@ -213,7 +292,18 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
ReactiveAuthenticationManager authenticationManager = (ReactiveAuthenticationManager) ReflectionTestUtils
|
||||
.getField(webFilter, "authenticationManager");
|
||||
assertThat(authenticationManager).isInstanceOf(JwtReactiveAuthenticationManager.class);
|
||||
}
|
||||
|
||||
private void assertFilterConfiguredWithOpaqueTokenAuthenticationManager(
|
||||
AssertableReactiveWebApplicationContext context) {
|
||||
MatcherSecurityWebFilterChain filterChain = (MatcherSecurityWebFilterChain) context
|
||||
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||
Stream<WebFilter> filters = filterChain.getWebFilters().toStream();
|
||||
AuthenticationWebFilter webFilter = (AuthenticationWebFilter) filters
|
||||
.filter((f) -> f instanceof AuthenticationWebFilter).findFirst().orElse(null);
|
||||
ReactiveAuthenticationManager authenticationManager = (ReactiveAuthenticationManager) ReflectionTestUtils
|
||||
.getField(webFilter, "authenticationManager");
|
||||
assertThat(authenticationManager).isInstanceOf(OAuth2IntrospectionReactiveAuthenticationManager.class);
|
||||
}
|
||||
|
||||
private String cleanIssuerPath(String issuer) {
|
||||
|
@ -269,13 +359,23 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class OAuth2TokenIntrospectionClientConfig {
|
||||
|
||||
@Bean
|
||||
public ReactiveOAuth2TokenIntrospectionClient decoder() {
|
||||
return mock(ReactiveOAuth2TokenIntrospectionClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class SecurityWebFilterChainConfig {
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain testSpringSecurityFilterChain(ServerHttpSecurity http, ReactiveJwtDecoder decoder) {
|
||||
SecurityWebFilterChain testSpringSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange().pathMatchers("/message/**").hasRole("ADMIN").anyExchange().authenticated().and()
|
||||
.oauth2ResourceServer().jwt().jwtDecoder(decoder);
|
||||
.httpBasic();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ import org.springframework.security.config.BeanIds;
|
|||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
@ -221,6 +223,68 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
.run((context) -> assertThat(getBearerTokenFilter(context)).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionClient() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://check-token.com",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-id=my-client-id",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-secret=my-client-secret")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(OAuth2TokenIntrospectionClient.class);
|
||||
assertThat(getBearerTokenFilter(context)).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void oAuth2TokenIntrospectionClientIsConditionalOnMissingBean() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://check-token.com")
|
||||
.withUserConfiguration(OAuth2TokenIntrospectionClientConfig.class)
|
||||
.run((context) -> assertThat(getBearerTokenFilter(context)).isNotNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenIntrospectionUriAvailableShouldBeConditionalOnClass() {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(OAuth2IntrospectionAuthenticationToken.class))
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://check-token.com",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-id=my-client-id",
|
||||
"spring.security.oauth2.resourceserver.opaque-token.client-secret=my-client-secret")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(OAuth2TokenIntrospectionClient.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenBothJwkSetUriAndTokenIntrospectionUriSetShouldFail() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.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 opaque-token.introspection-uri should be configured."));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenBothJwtIssuerUriAndTokenIntrospectionUriSetShouldFail() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.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 opaque-token.introspection-uri should be configured."));
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoConfigurationWhenBothJwtKeyLocationAndTokenIntrospectionUriSetShouldFail() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.security.oauth2.resourceserver.opaque-token.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 opaque-token.introspection-uri should be configured."));
|
||||
}
|
||||
|
||||
private Filter getBearerTokenFilter(AssertableWebApplicationContext context) {
|
||||
FilterChainProxy filterChain = (FilterChainProxy) context.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||
List<SecurityFilterChain> filterChains = filterChain.getFilterChains();
|
||||
|
@ -278,4 +342,15 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebSecurity
|
||||
static class OAuth2TokenIntrospectionClientConfig {
|
||||
|
||||
@Bean
|
||||
public OAuth2TokenIntrospectionClient decoder() {
|
||||
return mock(OAuth2TokenIntrospectionClient.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3801,8 +3801,8 @@ In other words, the two configurations in the following example use the Google p
|
|||
[[boot-features-security-oauth2-server]]
|
||||
==== Resource Server
|
||||
If you have `spring-security-oauth2-resource-server` on your classpath, Spring Boot can
|
||||
set up an OAuth2 Resource Server as long as a JWK Set URI or OIDC Issuer URI is specified,
|
||||
as shown in the following examples:
|
||||
set up an OAuth2 Resource Server. For JWT configuration, a JWK Set URI or OIDC Issuer URI
|
||||
needs to be specified, as shown in the following examples:
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
|
@ -3825,7 +3825,20 @@ The same properties are applicable for both servlet and reactive applications.
|
|||
Alternatively, you can define your own `JwtDecoder` bean for servlet applications
|
||||
or a `ReactiveJwtDecoder` for reactive applications.
|
||||
|
||||
In cases where opaque tokens are used instead of JWTs, you can configure the following properties
|
||||
to validate tokens via introspection:
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
spring.security.oauth2.resourceserver.opaque-token.introspection-uri=https://example.com/check-token
|
||||
spring.security.oauth2.resourceserver.opaque-token.client-id=my-client-id
|
||||
spring.security.oauth2.resourceserver.opaque-token.client-secret-my-client-secret
|
||||
----
|
||||
|
||||
Again, the same properties are applicable for both servlet and reactive applications.
|
||||
|
||||
Alternatively, you can define your own `OAuth2TokenIntrospectionClient` bean for servlet applications
|
||||
or a `ReactiveOAuth2TokenIntrospectionClient` for reactive applications.
|
||||
|
||||
==== Authorization Server
|
||||
Currently, Spring Security does not provide support for implementing an OAuth 2.0
|
||||
|
|
Loading…
Reference in New Issue