Remove Spring Security OAuth Auto-Configuration
This commit removes auto-configuration support for Spring Security OAuth, paving the way for the introduction of auto-configuration for Spring Security 5's new OAuth-related features. Closes gh-10255
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.authserver.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security OAuth2.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurer.class })
|
||||
@Import({ OAuth2AuthorizationServerConfiguration.class,
|
||||
OAuth2MethodSecurityConfiguration.class, OAuth2ResourceServerConfiguration.class,
|
||||
OAuth2RestOperationsConfiguration.class })
|
||||
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(OAuth2ClientProperties.class)
|
||||
public class OAuth2AutoConfiguration {
|
||||
|
||||
private final OAuth2ClientProperties credentials;
|
||||
|
||||
public OAuth2AutoConfiguration(OAuth2ClientProperties credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceServerProperties resourceServerProperties() {
|
||||
return new ResourceServerProperties(this.credentials.getClientId(),
|
||||
this.credentials.getClientSecret());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for OAuth2 Client.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "security.oauth2.client")
|
||||
public class OAuth2ClientProperties {
|
||||
|
||||
/**
|
||||
* OAuth2 client id.
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* OAuth2 client secret. A random secret is generated by default.
|
||||
*/
|
||||
private String clientSecret = UUID.randomUUID().toString();
|
||||
|
||||
private boolean defaultSecret = true;
|
||||
|
||||
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;
|
||||
this.defaultSecret = false;
|
||||
}
|
||||
|
||||
public boolean isDefaultSecret() {
|
||||
return this.defaultSecret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.authserver;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for OAuth2 Authorization server.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "security.oauth2.authorization")
|
||||
public class AuthorizationServerProperties {
|
||||
|
||||
/**
|
||||
* Spring Security access rule for the check token endpoint (e.g. a SpEL expression
|
||||
* like "isAuthenticated()") . Default is empty, which is interpreted as "denyAll()"
|
||||
* (no access).
|
||||
*/
|
||||
private String checkTokenAccess;
|
||||
|
||||
/**
|
||||
* Spring Security access rule for the token key endpoint (e.g. a SpEL expression like
|
||||
* "isAuthenticated()"). Default is empty, which is interpreted as "denyAll()" (no
|
||||
* access).
|
||||
*/
|
||||
private String tokenKeyAccess;
|
||||
|
||||
/**
|
||||
* Realm name for client authentication. If an unauthenticated request comes in to the
|
||||
* token endpoint, it will respond with a challenge including this name.
|
||||
*/
|
||||
private String realm;
|
||||
|
||||
public String getCheckTokenAccess() {
|
||||
return this.checkTokenAccess;
|
||||
}
|
||||
|
||||
public void setCheckTokenAccess(String checkTokenAccess) {
|
||||
this.checkTokenAccess = checkTokenAccess;
|
||||
}
|
||||
|
||||
public String getTokenKeyAccess() {
|
||||
return this.tokenKeyAccess;
|
||||
}
|
||||
|
||||
public void setTokenKeyAccess(String tokenKeyAccess) {
|
||||
this.tokenKeyAccess = tokenKeyAccess;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return this.realm;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.authserver;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.oauth2.config.annotation.builders.ClientDetailsServiceBuilder;
|
||||
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
|
||||
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
|
||||
/**
|
||||
* Configuration for a Spring Security OAuth2 authorization server. Back off if another
|
||||
* {@link AuthorizationServerConfigurer} already exists or if authorization server is not
|
||||
* enabled.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(EnableAuthorizationServer.class)
|
||||
@ConditionalOnMissingBean(AuthorizationServerConfigurer.class)
|
||||
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class)
|
||||
@EnableConfigurationProperties(AuthorizationServerProperties.class)
|
||||
public class OAuth2AuthorizationServerConfiguration
|
||||
extends AuthorizationServerConfigurerAdapter {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(OAuth2AuthorizationServerConfiguration.class);
|
||||
|
||||
private final BaseClientDetails details;
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
private final TokenStore tokenStore;
|
||||
|
||||
private final AccessTokenConverter tokenConverter;
|
||||
|
||||
private final AuthorizationServerProperties properties;
|
||||
|
||||
public OAuth2AuthorizationServerConfiguration(BaseClientDetails details,
|
||||
AuthenticationManager authenticationManager,
|
||||
ObjectProvider<TokenStore> tokenStore,
|
||||
ObjectProvider<AccessTokenConverter> tokenConverter,
|
||||
AuthorizationServerProperties properties) {
|
||||
this.details = details;
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.tokenStore = tokenStore.getIfAvailable();
|
||||
this.tokenConverter = tokenConverter.getIfAvailable();
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
|
||||
ClientDetailsServiceBuilder<InMemoryClientDetailsServiceBuilder>.ClientBuilder builder = clients
|
||||
.inMemory().withClient(this.details.getClientId());
|
||||
builder.secret(this.details.getClientSecret())
|
||||
.resourceIds(this.details.getResourceIds().toArray(new String[0]))
|
||||
.authorizedGrantTypes(
|
||||
this.details.getAuthorizedGrantTypes().toArray(new String[0]))
|
||||
.authorities(
|
||||
AuthorityUtils.authorityListToSet(this.details.getAuthorities())
|
||||
.toArray(new String[0]))
|
||||
.scopes(this.details.getScope().toArray(new String[0]));
|
||||
|
||||
if (this.details.getAutoApproveScopes() != null) {
|
||||
builder.autoApprove(
|
||||
this.details.getAutoApproveScopes().toArray(new String[0]));
|
||||
}
|
||||
if (this.details.getAccessTokenValiditySeconds() != null) {
|
||||
builder.accessTokenValiditySeconds(
|
||||
this.details.getAccessTokenValiditySeconds());
|
||||
}
|
||||
if (this.details.getRefreshTokenValiditySeconds() != null) {
|
||||
builder.refreshTokenValiditySeconds(
|
||||
this.details.getRefreshTokenValiditySeconds());
|
||||
}
|
||||
if (this.details.getRegisteredRedirectUri() != null) {
|
||||
builder.redirectUris(
|
||||
this.details.getRegisteredRedirectUri().toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
|
||||
throws Exception {
|
||||
if (this.tokenConverter != null) {
|
||||
endpoints.accessTokenConverter(this.tokenConverter);
|
||||
}
|
||||
if (this.tokenStore != null) {
|
||||
endpoints.tokenStore(this.tokenStore);
|
||||
}
|
||||
if (this.details.getAuthorizedGrantTypes().contains("password")) {
|
||||
endpoints.authenticationManager(this.authenticationManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerSecurityConfigurer security)
|
||||
throws Exception {
|
||||
if (this.properties.getCheckTokenAccess() != null) {
|
||||
security.checkTokenAccess(this.properties.getCheckTokenAccess());
|
||||
}
|
||||
if (this.properties.getTokenKeyAccess() != null) {
|
||||
security.tokenKeyAccess(this.properties.getTokenKeyAccess());
|
||||
}
|
||||
if (this.properties.getRealm() != null) {
|
||||
security.realm(this.properties.getRealm());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class ClientDetailsLogger {
|
||||
|
||||
private final OAuth2ClientProperties credentials;
|
||||
|
||||
protected ClientDetailsLogger(OAuth2ClientProperties credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
String prefix = "security.oauth2.client";
|
||||
boolean defaultSecret = this.credentials.isDefaultSecret();
|
||||
logger.info(String.format(
|
||||
"Initialized OAuth2 Client%n%n%s.client-id = %s%n"
|
||||
+ "%s.client-secret = %s%n%n",
|
||||
prefix, this.credentials.getClientId(), prefix,
|
||||
defaultSecret ? this.credentials.getClientSecret() : "****"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(BaseClientDetails.class)
|
||||
protected static class BaseClientDetailsConfiguration {
|
||||
|
||||
private final OAuth2ClientProperties client;
|
||||
|
||||
protected BaseClientDetailsConfiguration(OAuth2ClientProperties client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "security.oauth2.client")
|
||||
public BaseClientDetails oauth2ClientDetails() {
|
||||
BaseClientDetails details = new BaseClientDetails();
|
||||
if (this.client.getClientId() == null) {
|
||||
this.client.setClientId(UUID.randomUUID().toString());
|
||||
}
|
||||
details.setClientId(this.client.getClientId());
|
||||
details.setClientSecret(this.client.getClientSecret());
|
||||
details.setAuthorizedGrantTypes(Arrays.asList("authorization_code",
|
||||
"password", "client_credentials", "implicit", "refresh_token"));
|
||||
details.setAuthorities(
|
||||
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
|
||||
details.setRegisteredRedirectUri(Collections.<String>emptySet());
|
||||
return details;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
|
||||
|
||||
/**
|
||||
* Enable OAuth2 Single Sign On (SSO). If there is an existing
|
||||
* {@link WebSecurityConfigurerAdapter} provided by the user and annotated with
|
||||
* {@code @EnableOAuth2Sso}, it is enhanced by adding an authentication filter and an
|
||||
* authentication entry point. If the user only has {@code @EnableOAuth2Sso} but not on a
|
||||
* WebSecurityConfigurerAdapter then one is added with all paths secured.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@EnableOAuth2Client
|
||||
@EnableConfigurationProperties(OAuth2SsoProperties.class)
|
||||
@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class,
|
||||
ResourceServerTokenServicesConfiguration.class })
|
||||
public @interface EnableOAuth2Sso {
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* Condition that checks for {@link EnableOAuth2Sso} on a
|
||||
* {@link WebSecurityConfigurerAdapter}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
class EnableOAuth2SsoCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
String[] enablers = context.getBeanFactory()
|
||||
.getBeanNamesForAnnotation(EnableOAuth2Sso.class);
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("@EnableOAuth2Sso Condition");
|
||||
for (String name : enablers) {
|
||||
if (context.getBeanFactory().isTypeMatch(name,
|
||||
WebSecurityConfigurerAdapter.class)) {
|
||||
return ConditionOutcome.match(message
|
||||
.found("@EnableOAuth2Sso annotation on WebSecurityConfigurerAdapter")
|
||||
.items(name));
|
||||
}
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.didNotFind(
|
||||
"@EnableOAuth2Sso annotation " + "on any WebSecurityConfigurerAdapter")
|
||||
.atAll());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
|
||||
|
||||
/**
|
||||
* Shared {@link AuthorizationCodeResourceDetails} configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration
|
||||
class OAuth2ProtectedResourceDetailsConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "security.oauth2.client")
|
||||
@Primary
|
||||
public AuthorizationCodeResourceDetails oauth2RemoteResource() {
|
||||
return new AuthorizationCodeResourceDetails();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
||||
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
|
||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration for OAuth2 Single Sign On REST operations.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Madhura Bhave
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(EnableOAuth2Client.class)
|
||||
public class OAuth2RestOperationsConfiguration {
|
||||
|
||||
@Configuration
|
||||
@Conditional(ClientCredentialsCondition.class)
|
||||
protected static class SingletonScopedConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "security.oauth2.client")
|
||||
@Primary
|
||||
public ClientCredentialsResourceDetails oauth2RemoteResource() {
|
||||
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
|
||||
return details;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultOAuth2ClientContext oauth2ClientContext() {
|
||||
return new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(OAuth2ClientConfiguration.class)
|
||||
@Conditional({ OAuth2ClientIdCondition.class, NoClientCredentialsCondition.class })
|
||||
@Import(OAuth2ProtectedResourceDetailsConfiguration.class)
|
||||
protected static class SessionScopedConfiguration {
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(
|
||||
OAuth2ClientContextFilter filter, SecurityProperties security) {
|
||||
FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<>();
|
||||
registration.setFilter(filter);
|
||||
registration.setOrder(security.getFilter().getOrder() - 10);
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class ClientContextConfiguration {
|
||||
|
||||
private final AccessTokenRequest accessTokenRequest;
|
||||
|
||||
public ClientContextConfiguration(
|
||||
@Qualifier("accessTokenRequest") ObjectProvider<AccessTokenRequest> accessTokenRequest) {
|
||||
this.accessTokenRequest = accessTokenRequest.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
|
||||
public DefaultOAuth2ClientContext oauth2ClientContext() {
|
||||
return new DefaultOAuth2ClientContext(this.accessTokenRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// When the authentication is per cookie but the stored token is an oauth2 one, we can
|
||||
// pass that on to a client that wants to call downstream. We don't even need an
|
||||
// OAuth2ClientContextFilter until we need to refresh the access token. To handle
|
||||
// refresh tokens you need to @EnableOAuth2Client
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(OAuth2ClientConfiguration.class)
|
||||
@Conditional({ OAuth2ClientIdCondition.class, NoClientCredentialsCondition.class })
|
||||
@Import(OAuth2ProtectedResourceDetailsConfiguration.class)
|
||||
protected static class RequestScopedConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
|
||||
public DefaultOAuth2ClientContext oauth2ClientContext() {
|
||||
DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext(
|
||||
new DefaultAccessTokenRequest());
|
||||
Authentication principal = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
if (principal instanceof OAuth2Authentication) {
|
||||
OAuth2Authentication authentication = (OAuth2Authentication) principal;
|
||||
Object details = authentication.getDetails();
|
||||
if (details instanceof OAuth2AuthenticationDetails) {
|
||||
OAuth2AuthenticationDetails oauthsDetails = (OAuth2AuthenticationDetails) details;
|
||||
String token = oauthsDetails.getTokenValue();
|
||||
context.setAccessToken(new DefaultOAuth2AccessToken(token));
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to check if a {@code security.oauth2.client.client-id} is specified.
|
||||
*/
|
||||
static class OAuth2ClientIdCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
String clientId = context.getEnvironment()
|
||||
.getProperty("security.oauth2.client.client-id");
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("OAuth Client ID");
|
||||
if (StringUtils.hasLength(clientId)) {
|
||||
return ConditionOutcome.match(message
|
||||
.foundExactly("security.oauth2.client.client-id property"));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message
|
||||
.didNotFind("security.oauth2.client.client-id property").atAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to check for no client credentials.
|
||||
*/
|
||||
static class NoClientCredentialsCondition extends NoneNestedConditions {
|
||||
|
||||
NoClientCredentialsCondition() {
|
||||
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
}
|
||||
|
||||
@Conditional(ClientCredentialsCondition.class)
|
||||
static class ClientCredentialsActivated {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to check for client credentials.
|
||||
*/
|
||||
static class ClientCredentialsCondition extends AnyNestedCondition {
|
||||
|
||||
ClientCredentialsCondition() {
|
||||
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "security.oauth2.client", name = "grant-type", havingValue = "client_credentials", matchIfMissing = false)
|
||||
static class ClientCredentialsConfigured {
|
||||
}
|
||||
|
||||
@ConditionalOnNotWebApplication
|
||||
static class NoWebApplication {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Configuration for OAuth2 Single Sign On (SSO) when there is an existing
|
||||
* {@link WebSecurityConfigurerAdapter} provided by the user and annotated with
|
||||
* {@code @EnableOAuth2Sso}. The user-provided configuration is enhanced by adding an
|
||||
* authentication filter and an authentication entry point.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@Conditional(EnableOAuth2SsoCondition.class)
|
||||
public class OAuth2SsoCustomConfiguration
|
||||
implements ImportAware, BeanPostProcessor, ApplicationContextAware {
|
||||
|
||||
private Class<?> configType;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
this.configType = ClassUtils.resolveClassName(importMetadata.getClassName(),
|
||||
null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (this.configType.isAssignableFrom(bean.getClass())
|
||||
&& bean instanceof WebSecurityConfigurerAdapter) {
|
||||
ProxyFactory factory = new ProxyFactory();
|
||||
factory.setTarget(bean);
|
||||
factory.addAdvice(new SsoSecurityAdapter(this.applicationContext));
|
||||
bean = factory.getProxy();
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private static class SsoSecurityAdapter implements MethodInterceptor {
|
||||
|
||||
private SsoSecurityConfigurer configurer;
|
||||
|
||||
SsoSecurityAdapter(ApplicationContext applicationContext) {
|
||||
this.configurer = new SsoSecurityConfigurer(applicationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
if (invocation.getMethod().getName().equals("init")) {
|
||||
Method method = ReflectionUtils
|
||||
.findMethod(WebSecurityConfigurerAdapter.class, "getHttp");
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
HttpSecurity http = (HttpSecurity) ReflectionUtils.invokeMethod(method,
|
||||
invocation.getThis());
|
||||
this.configurer.configure(http);
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoDefaultConfiguration.NeedsWebSecurityCondition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* Configuration for OAuth2 Single Sign On (SSO). If the user only has
|
||||
* {@code @EnableOAuth2Sso} but not on a {@code WebSecurityConfigurerAdapter} then one is
|
||||
* added with all paths secured.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@Conditional(NeedsWebSecurityCondition.class)
|
||||
public class OAuth2SsoDefaultConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public OAuth2SsoDefaultConfiguration(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated();
|
||||
new SsoSecurityConfigurer(this.applicationContext).configure(http);
|
||||
}
|
||||
|
||||
protected static class NeedsWebSecurityCondition extends EnableOAuth2SsoCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
return ConditionOutcome.inverse(super.getMatchOutcome(context, metadata));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for OAuth2 Single Sign On (SSO).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "security.oauth2.sso")
|
||||
public class OAuth2SsoProperties {
|
||||
|
||||
public static final String DEFAULT_LOGIN_PATH = "/login";
|
||||
|
||||
/**
|
||||
* Path to the login page, i.e. the one that triggers the redirect to the OAuth2
|
||||
* Authorization Server.
|
||||
*/
|
||||
private String loginPath = DEFAULT_LOGIN_PATH;
|
||||
|
||||
public String getLoginPath() {
|
||||
return this.loginPath;
|
||||
}
|
||||
|
||||
public void setLoginPath(String loginPath) {
|
||||
this.loginPath = loginPath;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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.client;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestOperations;
|
||||
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
|
||||
/**
|
||||
* Configurer for OAuth2 Single Sign On (SSO).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
class SsoSecurityConfigurer {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
SsoSecurityConfigurer(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
OAuth2SsoProperties sso = this.applicationContext
|
||||
.getBean(OAuth2SsoProperties.class);
|
||||
// Delay the processing of the filter until we know the
|
||||
// SessionAuthenticationStrategy is available:
|
||||
http.apply(new OAuth2ClientAuthenticationConfigurer(oauth2SsoFilter(sso)));
|
||||
addAuthenticationEntryPoint(http, sso);
|
||||
}
|
||||
|
||||
private void addAuthenticationEntryPoint(HttpSecurity http, OAuth2SsoProperties sso)
|
||||
throws Exception {
|
||||
ExceptionHandlingConfigurer<HttpSecurity> exceptions = http.exceptionHandling();
|
||||
ContentNegotiationStrategy contentNegotiationStrategy = http
|
||||
.getSharedObject(ContentNegotiationStrategy.class);
|
||||
if (contentNegotiationStrategy == null) {
|
||||
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
|
||||
}
|
||||
MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(
|
||||
contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML,
|
||||
new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
|
||||
preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
|
||||
exceptions.defaultAuthenticationEntryPointFor(
|
||||
new LoginUrlAuthenticationEntryPoint(sso.getLoginPath()),
|
||||
preferredMatcher);
|
||||
// When multiple entry points are provided the default is the first one
|
||||
exceptions.defaultAuthenticationEntryPointFor(
|
||||
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
||||
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
|
||||
}
|
||||
|
||||
private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter(
|
||||
OAuth2SsoProperties sso) {
|
||||
OAuth2RestOperations restTemplate = this.applicationContext
|
||||
.getBean(UserInfoRestTemplateFactory.class).getUserInfoRestTemplate();
|
||||
ResourceServerTokenServices tokenServices = this.applicationContext
|
||||
.getBean(ResourceServerTokenServices.class);
|
||||
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
|
||||
sso.getLoginPath());
|
||||
filter.setRestTemplate(restTemplate);
|
||||
filter.setTokenServices(tokenServices);
|
||||
filter.setApplicationEventPublisher(this.applicationContext);
|
||||
return filter;
|
||||
}
|
||||
|
||||
private static class OAuth2ClientAuthenticationConfigurer
|
||||
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
|
||||
|
||||
private OAuth2ClientAuthenticationProcessingFilter filter;
|
||||
|
||||
OAuth2ClientAuthenticationConfigurer(
|
||||
OAuth2ClientAuthenticationProcessingFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity builder) throws Exception {
|
||||
OAuth2ClientAuthenticationProcessingFilter ssoFilter = this.filter;
|
||||
ssoFilter.setSessionAuthenticationStrategy(
|
||||
builder.getSharedObject(SessionAuthenticationStrategy.class));
|
||||
builder.addFilterAfter(ssoFilter,
|
||||
AbstractPreAuthenticatedProcessingFilter.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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.method;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
|
||||
|
||||
/**
|
||||
* Auto-configure an expression handler for method-level security (if the user already has
|
||||
* {@code @EnableGlobalMethodSecurity}).
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ OAuth2AccessToken.class })
|
||||
@ConditionalOnBean(GlobalMethodSecurityConfiguration.class)
|
||||
public class OAuth2MethodSecurityConfiguration
|
||||
implements BeanFactoryPostProcessor, ApplicationContextAware {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
OAuth2ExpressionHandlerInjectionPostProcessor processor = new OAuth2ExpressionHandlerInjectionPostProcessor(
|
||||
this.applicationContext);
|
||||
beanFactory.addBeanPostProcessor(processor);
|
||||
}
|
||||
|
||||
private static class OAuth2ExpressionHandlerInjectionPostProcessor
|
||||
implements BeanPostProcessor {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
OAuth2ExpressionHandlerInjectionPostProcessor(
|
||||
ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof DefaultMethodSecurityExpressionHandler
|
||||
&& !(bean instanceof OAuth2MethodSecurityExpressionHandler)) {
|
||||
return getExpressionHandler(
|
||||
(DefaultMethodSecurityExpressionHandler) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private OAuth2MethodSecurityExpressionHandler getExpressionHandler(
|
||||
DefaultMethodSecurityExpressionHandler bean) {
|
||||
OAuth2MethodSecurityExpressionHandler handler = new OAuth2MethodSecurityExpressionHandler();
|
||||
handler.setApplicationContext(this.applicationContext);
|
||||
AuthenticationTrustResolver trustResolver = findInContext(
|
||||
AuthenticationTrustResolver.class);
|
||||
if (trustResolver != null) {
|
||||
handler.setTrustResolver(trustResolver);
|
||||
}
|
||||
handler.setExpressionParser(bean.getExpressionParser());
|
||||
return handler;
|
||||
}
|
||||
|
||||
private <T> T findInContext(Class<T> type) {
|
||||
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
this.applicationContext, type).length == 1) {
|
||||
return this.applicationContext.getBean(type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* Strategy used by {@link UserInfoTokenServices} to extract authorities from the resource
|
||||
* server's response.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AuthoritiesExtractor {
|
||||
|
||||
/**
|
||||
* Extract the authorities from the resource server's response.
|
||||
* @param map the response
|
||||
* @return the extracted authorities
|
||||
*/
|
||||
List<GrantedAuthority> extractAuthorities(Map<String, Object> map);
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.AcceptJsonRequestEnhancer;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.AcceptJsonRequestInterceptor;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
|
||||
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Factory used to create the {@link OAuth2RestTemplate} used for extracting user info
|
||||
* during authentication if none is available.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class DefaultUserInfoRestTemplateFactory implements UserInfoRestTemplateFactory {
|
||||
|
||||
private static final AuthorizationCodeResourceDetails DEFAULT_RESOURCE_DETAILS;
|
||||
|
||||
static {
|
||||
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
|
||||
details.setClientId("<N/A>");
|
||||
details.setUserAuthorizationUri("Not a URI because there is no client");
|
||||
details.setAccessTokenUri("Not a URI because there is no client");
|
||||
DEFAULT_RESOURCE_DETAILS = details;
|
||||
}
|
||||
|
||||
private final List<UserInfoRestTemplateCustomizer> customizers;
|
||||
|
||||
private final OAuth2ProtectedResourceDetails details;
|
||||
|
||||
private final OAuth2ClientContext oauth2ClientContext;
|
||||
|
||||
private OAuth2RestTemplate oauth2RestTemplate;
|
||||
|
||||
public DefaultUserInfoRestTemplateFactory(
|
||||
ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers,
|
||||
ObjectProvider<OAuth2ProtectedResourceDetails> details,
|
||||
ObjectProvider<OAuth2ClientContext> oauth2ClientContext) {
|
||||
this.customizers = customizers.getIfAvailable();
|
||||
this.details = details.getIfAvailable();
|
||||
this.oauth2ClientContext = oauth2ClientContext.getIfAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2RestTemplate getUserInfoRestTemplate() {
|
||||
if (this.oauth2RestTemplate == null) {
|
||||
this.oauth2RestTemplate = createOAuth2RestTemplate(
|
||||
this.details == null ? DEFAULT_RESOURCE_DETAILS : this.details);
|
||||
this.oauth2RestTemplate.getInterceptors()
|
||||
.add(new AcceptJsonRequestInterceptor());
|
||||
AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
|
||||
accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer());
|
||||
this.oauth2RestTemplate.setAccessTokenProvider(accessTokenProvider);
|
||||
if (!CollectionUtils.isEmpty(this.customizers)) {
|
||||
AnnotationAwareOrderComparator.sort(this.customizers);
|
||||
for (UserInfoRestTemplateCustomizer customizer : this.customizers) {
|
||||
customizer.customize(this.oauth2RestTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.oauth2RestTemplate;
|
||||
}
|
||||
|
||||
private OAuth2RestTemplate createOAuth2RestTemplate(
|
||||
OAuth2ProtectedResourceDetails details) {
|
||||
if (this.oauth2ClientContext == null) {
|
||||
return new OAuth2RestTemplate(details);
|
||||
}
|
||||
return new OAuth2RestTemplate(details, this.oauth2ClientContext);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link AuthoritiesExtractor}. Extracts the authorities from
|
||||
* the map with the key {@code authorities}. If no such value exists, a single
|
||||
* {@code ROLE_USER} authority is returned.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class FixedAuthoritiesExtractor implements AuthoritiesExtractor {
|
||||
|
||||
private static final String AUTHORITIES = "authorities";
|
||||
|
||||
private static final String[] AUTHORITY_KEYS = { "authority", "role", "value" };
|
||||
|
||||
@Override
|
||||
public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
|
||||
String authorities = "ROLE_USER";
|
||||
if (map.containsKey(AUTHORITIES)) {
|
||||
authorities = asAuthorities(map.get(AUTHORITIES));
|
||||
}
|
||||
return AuthorityUtils.commaSeparatedStringToAuthorityList(authorities);
|
||||
}
|
||||
|
||||
private String asAuthorities(Object object) {
|
||||
List<Object> authorities = new ArrayList<>();
|
||||
if (object instanceof Collection) {
|
||||
Collection<?> collection = (Collection<?>) object;
|
||||
object = collection.toArray(new Object[0]);
|
||||
}
|
||||
if (ObjectUtils.isArray(object)) {
|
||||
Object[] array = (Object[]) object;
|
||||
for (Object value : array) {
|
||||
if (value instanceof String) {
|
||||
authorities.add(value);
|
||||
}
|
||||
else if (value instanceof Map) {
|
||||
authorities.add(asAuthority((Map<?, ?>) value));
|
||||
}
|
||||
else {
|
||||
authorities.add(value);
|
||||
}
|
||||
}
|
||||
return StringUtils.collectionToCommaDelimitedString(authorities);
|
||||
}
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
private Object asAuthority(Map<?, ?> map) {
|
||||
if (map.size() == 1) {
|
||||
return map.values().iterator().next();
|
||||
}
|
||||
for (String key : AUTHORITY_KEYS) {
|
||||
if (map.containsKey(key)) {
|
||||
return map.get(key);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link PrincipalExtractor}. Extracts the principal from the
|
||||
* map with well known keys.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class FixedPrincipalExtractor implements PrincipalExtractor {
|
||||
|
||||
private static final String[] PRINCIPAL_KEYS = new String[] { "user", "username",
|
||||
"userid", "user_id", "login", "id", "name" };
|
||||
|
||||
@Override
|
||||
public Object extractPrincipal(Map<String, Object> map) {
|
||||
for (String key : PRINCIPAL_KEYS) {
|
||||
if (map.containsKey(key)) {
|
||||
return map.get(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to provide additional configuration to the
|
||||
* {@link JwtAccessTokenConverter}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JwtAccessTokenConverterConfigurer {
|
||||
|
||||
/**
|
||||
* Configure the {@link JwtAccessTokenConverter}.
|
||||
* @param converter the converter to configure
|
||||
*/
|
||||
void configure(JwtAccessTokenConverter converter);
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Callback for customizing the {@link RestTemplate} that is used to fetch the keys used
|
||||
* by {@link JwtAccessTokenConverter}.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.5.2
|
||||
* @see JwtAccessTokenConverter#setSigningKey(String)
|
||||
* @see JwtAccessTokenConverter#setVerifierKey(String)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JwtAccessTokenConverterRestTemplateCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the {@code template} before it is initialized.
|
||||
* @param template the rest template
|
||||
*/
|
||||
void customize(RestTemplate template);
|
||||
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration.ResourceServerCondition;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ConfigurationCondition;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Auto-configure a Spring Security OAuth2 resource server. Back off if another
|
||||
* {@link ResourceServerConfigurer} already exists or if resource server not enabled.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Dave Syer
|
||||
* @author Madhura Bhave
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@Conditional(ResourceServerCondition.class)
|
||||
@ConditionalOnClass({ EnableResourceServer.class, SecurityProperties.class })
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnBean(ResourceServerConfiguration.class)
|
||||
@Import(ResourceServerTokenServicesConfiguration.class)
|
||||
public class OAuth2ResourceServerConfiguration {
|
||||
|
||||
private final ResourceServerProperties resource;
|
||||
|
||||
public OAuth2ResourceServerConfiguration(ResourceServerProperties resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ResourceServerConfigurer.class)
|
||||
public ResourceServerConfigurer resourceServer() {
|
||||
return new ResourceSecurityConfigurer(this.resource);
|
||||
}
|
||||
|
||||
protected static class ResourceSecurityConfigurer
|
||||
extends ResourceServerConfigurerAdapter {
|
||||
|
||||
private ResourceServerProperties resource;
|
||||
|
||||
public ResourceSecurityConfigurer(ResourceServerProperties resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ResourceServerSecurityConfigurer resources)
|
||||
throws Exception {
|
||||
resources.resourceId(this.resource.getResourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().authenticated();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class ResourceServerCondition extends SpringBootCondition
|
||||
implements ConfigurationCondition {
|
||||
|
||||
private static final Bindable<Map<String, Object>> STRING_OBJECT_MAP = Bindable
|
||||
.mapOf(String.class, Object.class);
|
||||
|
||||
private static final String AUTHORIZATION_ANNOTATION = "org.springframework."
|
||||
+ "security.oauth2.config.annotation.web.configuration."
|
||||
+ "AuthorizationServerEndpointsConfiguration";
|
||||
|
||||
@Override
|
||||
public ConfigurationPhase getConfigurationPhase() {
|
||||
return ConfigurationPhase.REGISTER_BEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("OAuth ResourceServer Condition");
|
||||
Environment environment = context.getEnvironment();
|
||||
if (!(environment instanceof ConfigurableEnvironment)) {
|
||||
return ConditionOutcome
|
||||
.noMatch(message.didNotFind("A ConfigurableEnvironment").atAll());
|
||||
}
|
||||
if (hasOAuthClientId(environment)) {
|
||||
return ConditionOutcome.match(message.foundExactly("client-id property"));
|
||||
}
|
||||
Binder binder = Binder.get(environment);
|
||||
String prefix = "security.oauth2.resource.";
|
||||
if (binder.bind(prefix + "jwt", STRING_OBJECT_MAP).isBound()) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("JWT resource configuration"));
|
||||
}
|
||||
if (binder.bind(prefix + "jwk", STRING_OBJECT_MAP).isBound()) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("JWK resource configuration"));
|
||||
}
|
||||
if (StringUtils.hasText(environment.getProperty(prefix + "user-info-uri"))) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("user-info-uri property"));
|
||||
}
|
||||
if (StringUtils.hasText(environment.getProperty(prefix + "token-info-uri"))) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("token-info-uri property"));
|
||||
}
|
||||
if (ClassUtils.isPresent(AUTHORIZATION_ANNOTATION, null)) {
|
||||
if (AuthorizationServerEndpointsConfigurationBeanCondition
|
||||
.matches(context)) {
|
||||
return ConditionOutcome.match(
|
||||
message.found("class").items(AUTHORIZATION_ANNOTATION));
|
||||
}
|
||||
}
|
||||
return ConditionOutcome.noMatch(
|
||||
message.didNotFind("client ID, JWT resource or authorization server")
|
||||
.atAll());
|
||||
}
|
||||
|
||||
private boolean hasOAuthClientId(Environment environment) {
|
||||
return StringUtils.hasLength(
|
||||
environment.getProperty("security.oauth2.client.client-id"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class)
|
||||
private static class AuthorizationServerEndpointsConfigurationBeanCondition {
|
||||
|
||||
public static boolean matches(ConditionContext context) {
|
||||
Class<AuthorizationServerEndpointsConfigurationBeanCondition> type = AuthorizationServerEndpointsConfigurationBeanCondition.class;
|
||||
Conditional conditional = AnnotationUtils.findAnnotation(type,
|
||||
Conditional.class);
|
||||
StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(type);
|
||||
for (Class<? extends Condition> conditionType : conditional.value()) {
|
||||
Condition condition = BeanUtils.instantiateClass(conditionType);
|
||||
if (condition.matches(context, metadata)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Strategy used by {@link UserInfoTokenServices} to extract the principal from the
|
||||
* resource server's response.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PrincipalExtractor {
|
||||
|
||||
/**
|
||||
* Extract the principal that should be used for the token.
|
||||
* @param map the source map
|
||||
* @return the extracted principal or {@code null}
|
||||
*/
|
||||
Object extractPrincipal(Map<String, Object> map);
|
||||
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
||||
/**
|
||||
* Configuration properties for OAuth2 Resources.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Madhura Bhave
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "security.oauth2.resource")
|
||||
public class ResourceServerProperties implements BeanFactoryAware {
|
||||
|
||||
@JsonIgnore
|
||||
private final String clientId;
|
||||
|
||||
@JsonIgnore
|
||||
private final String clientSecret;
|
||||
|
||||
@JsonIgnore
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
private String serviceId = "resource";
|
||||
|
||||
/**
|
||||
* Identifier of the resource.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* URI of the user endpoint.
|
||||
*/
|
||||
private String userInfoUri;
|
||||
|
||||
/**
|
||||
* URI of the token decoding endpoint.
|
||||
*/
|
||||
private String tokenInfoUri;
|
||||
|
||||
/**
|
||||
* Use the token info, can be set to false to use the user info.
|
||||
*/
|
||||
private boolean preferTokenInfo = true;
|
||||
|
||||
/**
|
||||
* The token type to send when using the userInfoUri.
|
||||
*/
|
||||
private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;
|
||||
|
||||
private Jwt jwt = new Jwt();
|
||||
|
||||
private Jwk jwk = new Jwk();
|
||||
|
||||
public ResourceServerProperties() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public ResourceServerProperties(String clientId, String clientSecret) {
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (ListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getServiceId() {
|
||||
return this.serviceId;
|
||||
}
|
||||
|
||||
public void setServiceId(String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUserInfoUri() {
|
||||
return this.userInfoUri;
|
||||
}
|
||||
|
||||
public void setUserInfoUri(String userInfoUri) {
|
||||
this.userInfoUri = userInfoUri;
|
||||
}
|
||||
|
||||
public String getTokenInfoUri() {
|
||||
return this.tokenInfoUri;
|
||||
}
|
||||
|
||||
public void setTokenInfoUri(String tokenInfoUri) {
|
||||
this.tokenInfoUri = tokenInfoUri;
|
||||
}
|
||||
|
||||
public boolean isPreferTokenInfo() {
|
||||
return this.preferTokenInfo;
|
||||
}
|
||||
|
||||
public void setPreferTokenInfo(boolean preferTokenInfo) {
|
||||
this.preferTokenInfo = preferTokenInfo;
|
||||
}
|
||||
|
||||
public String getTokenType() {
|
||||
return this.tokenType;
|
||||
}
|
||||
|
||||
public void setTokenType(String tokenType) {
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
|
||||
public Jwt getJwt() {
|
||||
return this.jwt;
|
||||
}
|
||||
|
||||
public void setJwt(Jwt jwt) {
|
||||
this.jwt = jwt;
|
||||
}
|
||||
|
||||
public Jwk getJwk() {
|
||||
return this.jwk;
|
||||
}
|
||||
|
||||
public void setJwk(Jwk jwk) {
|
||||
this.jwk = jwk;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return this.clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return this.clientSecret;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void validate() {
|
||||
if (countBeans(AuthorizationServerEndpointsConfiguration.class) > 0) {
|
||||
// If we are an authorization server we don't need remote resource token
|
||||
// services
|
||||
return;
|
||||
}
|
||||
if (countBeans(ResourceServerTokenServicesConfiguration.class) == 0) {
|
||||
// If we are not a resource server or an SSO client we don't need remote
|
||||
// resource token services
|
||||
return;
|
||||
}
|
||||
if (!StringUtils.hasText(this.clientId)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
doValidate();
|
||||
}
|
||||
catch (BindException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private int countBeans(Class<?> type) {
|
||||
return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, type,
|
||||
true, false).length;
|
||||
}
|
||||
|
||||
private void doValidate() throws BindException {
|
||||
BindingResult errors = new BeanPropertyBindingResult(this,
|
||||
"resourceServerProperties");
|
||||
boolean jwtConfigPresent = StringUtils.hasText(this.jwt.getKeyUri())
|
||||
|| StringUtils.hasText(this.jwt.getKeyValue());
|
||||
boolean jwkConfigPresent = StringUtils.hasText(this.jwk.getKeySetUri());
|
||||
if (jwtConfigPresent && jwkConfigPresent) {
|
||||
errors.reject("ambiguous.keyUri",
|
||||
"Only one of jwt.keyUri (or jwt.keyValue) and jwk.keySetUri should"
|
||||
+ " be configured.");
|
||||
}
|
||||
if (!jwtConfigPresent && !jwkConfigPresent) {
|
||||
if (!StringUtils.hasText(this.userInfoUri)
|
||||
&& !StringUtils.hasText(this.tokenInfoUri)) {
|
||||
errors.rejectValue("tokenInfoUri", "missing.tokenInfoUri",
|
||||
"Missing tokenInfoUri and userInfoUri and there is no "
|
||||
+ "JWT verifier key");
|
||||
}
|
||||
if (StringUtils.hasText(this.tokenInfoUri) && isPreferTokenInfo()) {
|
||||
if (!StringUtils.hasText(this.clientSecret)) {
|
||||
errors.rejectValue("clientSecret", "missing.clientSecret",
|
||||
"Missing client secret");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errors.hasErrors()) {
|
||||
throw new BindException(errors);
|
||||
}
|
||||
}
|
||||
|
||||
public class Jwt {
|
||||
|
||||
/**
|
||||
* The verification key of the JWT token. Can either be a symmetric secret or
|
||||
* PEM-encoded RSA public key. If the value is not available, you can set the URI
|
||||
* instead.
|
||||
*/
|
||||
private String keyValue;
|
||||
|
||||
/**
|
||||
* The URI of the JWT token. Can be set if the value is not available and the key
|
||||
* is public.
|
||||
*/
|
||||
private String keyUri;
|
||||
|
||||
public String getKeyValue() {
|
||||
return this.keyValue;
|
||||
}
|
||||
|
||||
public void setKeyValue(String keyValue) {
|
||||
this.keyValue = keyValue;
|
||||
}
|
||||
|
||||
public void setKeyUri(String keyUri) {
|
||||
this.keyUri = keyUri;
|
||||
}
|
||||
|
||||
public String getKeyUri() {
|
||||
return this.keyUri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Jwk {
|
||||
|
||||
/**
|
||||
* The URI to get verification keys to verify the JWT token. This can be set when
|
||||
* the authorization server returns a set of verification keys.
|
||||
*/
|
||||
private String keySetUri;
|
||||
|
||||
public String getKeySetUri() {
|
||||
return this.keySetUri;
|
||||
}
|
||||
|
||||
public void setKeySetUri(String keySetUri) {
|
||||
this.keySetUri = keySetUri;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.security.oauth2.client.OAuth2ClientContext;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestOperations;
|
||||
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
|
||||
import org.springframework.security.oauth2.client.token.RequestEnhancer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore;
|
||||
import org.springframework.social.connect.ConnectionFactoryLocator;
|
||||
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Configuration for an OAuth2 resource server.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Madhura Bhave
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class)
|
||||
public class ResourceServerTokenServicesConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public UserInfoRestTemplateFactory userInfoRestTemplateFactory(
|
||||
ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers,
|
||||
ObjectProvider<OAuth2ProtectedResourceDetails> details,
|
||||
ObjectProvider<OAuth2ClientContext> oauth2ClientContext) {
|
||||
return new DefaultUserInfoRestTemplateFactory(customizers, details,
|
||||
oauth2ClientContext);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Conditional(RemoteTokenCondition.class)
|
||||
protected static class RemoteTokenServicesConfiguration {
|
||||
|
||||
@Configuration
|
||||
@Conditional(TokenInfoCondition.class)
|
||||
protected static class TokenInfoServicesConfiguration {
|
||||
|
||||
private final ResourceServerProperties resource;
|
||||
|
||||
protected TokenInfoServicesConfiguration(ResourceServerProperties resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoteTokenServices remoteTokenServices() {
|
||||
RemoteTokenServices services = new RemoteTokenServices();
|
||||
services.setCheckTokenEndpointUrl(this.resource.getTokenInfoUri());
|
||||
services.setClientId(this.resource.getClientId());
|
||||
services.setClientSecret(this.resource.getClientSecret());
|
||||
return services;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(OAuth2ConnectionFactory.class)
|
||||
@Conditional(NotTokenInfoCondition.class)
|
||||
protected static class SocialTokenServicesConfiguration {
|
||||
|
||||
private final ResourceServerProperties sso;
|
||||
|
||||
private final OAuth2ConnectionFactory<?> connectionFactory;
|
||||
|
||||
private final OAuth2RestOperations restTemplate;
|
||||
|
||||
private final AuthoritiesExtractor authoritiesExtractor;
|
||||
|
||||
private final PrincipalExtractor principalExtractor;
|
||||
|
||||
public SocialTokenServicesConfiguration(ResourceServerProperties sso,
|
||||
ObjectProvider<OAuth2ConnectionFactory<?>> connectionFactory,
|
||||
UserInfoRestTemplateFactory restTemplateFactory,
|
||||
ObjectProvider<AuthoritiesExtractor> authoritiesExtractor,
|
||||
ObjectProvider<PrincipalExtractor> principalExtractor) {
|
||||
this.sso = sso;
|
||||
this.connectionFactory = connectionFactory.getIfAvailable();
|
||||
this.restTemplate = restTemplateFactory.getUserInfoRestTemplate();
|
||||
this.authoritiesExtractor = authoritiesExtractor.getIfAvailable();
|
||||
this.principalExtractor = principalExtractor.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ConnectionFactoryLocator.class)
|
||||
@ConditionalOnMissingBean(ResourceServerTokenServices.class)
|
||||
public SpringSocialTokenServices socialTokenServices() {
|
||||
return new SpringSocialTokenServices(this.connectionFactory,
|
||||
this.sso.getClientId());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ ConnectionFactoryLocator.class,
|
||||
ResourceServerTokenServices.class })
|
||||
public UserInfoTokenServices userInfoTokenServices() {
|
||||
UserInfoTokenServices services = new UserInfoTokenServices(
|
||||
this.sso.getUserInfoUri(), this.sso.getClientId());
|
||||
services.setTokenType(this.sso.getTokenType());
|
||||
services.setRestTemplate(this.restTemplate);
|
||||
if (this.authoritiesExtractor != null) {
|
||||
services.setAuthoritiesExtractor(this.authoritiesExtractor);
|
||||
}
|
||||
if (this.principalExtractor != null) {
|
||||
services.setPrincipalExtractor(this.principalExtractor);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass("org.springframework.social.connect.support.OAuth2ConnectionFactory")
|
||||
@Conditional(NotTokenInfoCondition.class)
|
||||
protected static class UserInfoTokenServicesConfiguration {
|
||||
|
||||
private final ResourceServerProperties sso;
|
||||
|
||||
private final OAuth2RestOperations restTemplate;
|
||||
|
||||
private final AuthoritiesExtractor authoritiesExtractor;
|
||||
|
||||
private final PrincipalExtractor principalExtractor;
|
||||
|
||||
public UserInfoTokenServicesConfiguration(ResourceServerProperties sso,
|
||||
UserInfoRestTemplateFactory restTemplateFactory,
|
||||
ObjectProvider<AuthoritiesExtractor> authoritiesExtractor,
|
||||
ObjectProvider<PrincipalExtractor> principalExtractor) {
|
||||
this.sso = sso;
|
||||
this.restTemplate = restTemplateFactory.getUserInfoRestTemplate();
|
||||
this.authoritiesExtractor = authoritiesExtractor.getIfAvailable();
|
||||
this.principalExtractor = principalExtractor.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ResourceServerTokenServices.class)
|
||||
public UserInfoTokenServices userInfoTokenServices() {
|
||||
UserInfoTokenServices services = new UserInfoTokenServices(
|
||||
this.sso.getUserInfoUri(), this.sso.getClientId());
|
||||
services.setRestTemplate(this.restTemplate);
|
||||
services.setTokenType(this.sso.getTokenType());
|
||||
if (this.authoritiesExtractor != null) {
|
||||
services.setAuthoritiesExtractor(this.authoritiesExtractor);
|
||||
}
|
||||
if (this.principalExtractor != null) {
|
||||
services.setPrincipalExtractor(this.principalExtractor);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Conditional(JwkCondition.class)
|
||||
protected static class JwkTokenStoreConfiguration {
|
||||
|
||||
private final ResourceServerProperties resource;
|
||||
|
||||
public JwkTokenStoreConfiguration(ResourceServerProperties resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ResourceServerTokenServices.class)
|
||||
public DefaultTokenServices jwkTokenServices(TokenStore jwkTokenStore) {
|
||||
DefaultTokenServices services = new DefaultTokenServices();
|
||||
services.setTokenStore(jwkTokenStore);
|
||||
return services;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TokenStore.class)
|
||||
public TokenStore jwkTokenStore() {
|
||||
return new JwkTokenStore(this.resource.getJwk().getKeySetUri());
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Conditional(JwtTokenCondition.class)
|
||||
protected static class JwtTokenServicesConfiguration {
|
||||
|
||||
private final ResourceServerProperties resource;
|
||||
|
||||
private final List<JwtAccessTokenConverterConfigurer> configurers;
|
||||
|
||||
private final List<JwtAccessTokenConverterRestTemplateCustomizer> customizers;
|
||||
|
||||
public JwtTokenServicesConfiguration(ResourceServerProperties resource,
|
||||
ObjectProvider<List<JwtAccessTokenConverterConfigurer>> configurers,
|
||||
ObjectProvider<List<JwtAccessTokenConverterRestTemplateCustomizer>> customizers) {
|
||||
this.resource = resource;
|
||||
this.configurers = configurers.getIfAvailable();
|
||||
this.customizers = customizers.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ResourceServerTokenServices.class)
|
||||
public DefaultTokenServices jwtTokenServices(TokenStore jwtTokenStore) {
|
||||
DefaultTokenServices services = new DefaultTokenServices();
|
||||
services.setTokenStore(jwtTokenStore);
|
||||
return services;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TokenStore.class)
|
||||
public TokenStore jwtTokenStore() {
|
||||
return new JwtTokenStore(jwtTokenEnhancer());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtAccessTokenConverter jwtTokenEnhancer() {
|
||||
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
|
||||
String keyValue = this.resource.getJwt().getKeyValue();
|
||||
if (!StringUtils.hasText(keyValue)) {
|
||||
keyValue = getKeyFromServer();
|
||||
}
|
||||
if (StringUtils.hasText(keyValue) && !keyValue.startsWith("-----BEGIN")) {
|
||||
converter.setSigningKey(keyValue);
|
||||
}
|
||||
if (keyValue != null) {
|
||||
converter.setVerifierKey(keyValue);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(this.configurers)) {
|
||||
AnnotationAwareOrderComparator.sort(this.configurers);
|
||||
for (JwtAccessTokenConverterConfigurer configurer : this.configurers) {
|
||||
configurer.configure(converter);
|
||||
}
|
||||
}
|
||||
return converter;
|
||||
}
|
||||
|
||||
private String getKeyFromServer() {
|
||||
RestTemplate keyUriRestTemplate = new RestTemplate();
|
||||
if (!CollectionUtils.isEmpty(this.customizers)) {
|
||||
for (JwtAccessTokenConverterRestTemplateCustomizer customizer : this.customizers) {
|
||||
customizer.customize(keyUriRestTemplate);
|
||||
}
|
||||
}
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
String username = this.resource.getClientId();
|
||||
String password = this.resource.getClientSecret();
|
||||
if (username != null && password != null) {
|
||||
byte[] token = Base64.getEncoder()
|
||||
.encode((username + ":" + password).getBytes());
|
||||
headers.add("Authorization", "Basic " + new String(token));
|
||||
}
|
||||
HttpEntity<Void> request = new HttpEntity<>(headers);
|
||||
String url = this.resource.getJwt().getKeyUri();
|
||||
return (String) keyUriRestTemplate
|
||||
.exchange(url, HttpMethod.GET, request, Map.class).getBody()
|
||||
.get("value");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TokenInfoCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("OAuth TokenInfo Condition");
|
||||
Environment environment = context.getEnvironment();
|
||||
Boolean preferTokenInfo = environment.getProperty(
|
||||
"security.oauth2.resource.prefer-token-info", Boolean.class);
|
||||
if (preferTokenInfo == null) {
|
||||
preferTokenInfo = environment
|
||||
.resolvePlaceholders("${OAUTH2_RESOURCE_PREFERTOKENINFO:true}")
|
||||
.equals("true");
|
||||
}
|
||||
String tokenInfoUri = environment
|
||||
.getProperty("security.oauth2.resource.token-info-uri");
|
||||
String userInfoUri = environment
|
||||
.getProperty("security.oauth2.resource.user-info-uri");
|
||||
if (!StringUtils.hasLength(userInfoUri)
|
||||
&& !StringUtils.hasLength(tokenInfoUri)) {
|
||||
return ConditionOutcome
|
||||
.match(message.didNotFind("user-info-uri property").atAll());
|
||||
}
|
||||
if (StringUtils.hasLength(tokenInfoUri) && preferTokenInfo) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("preferred token-info-uri property"));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.didNotFind("token info").atAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class JwtTokenCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("OAuth JWT Condition");
|
||||
Environment environment = context.getEnvironment();
|
||||
String keyValue = environment
|
||||
.getProperty("security.oauth2.resource.jwt.key-value");
|
||||
String keyUri = environment
|
||||
.getProperty("security.oauth2.resource.jwt.key-uri");
|
||||
if (StringUtils.hasText(keyValue) || StringUtils.hasText(keyUri)) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("provided public key"));
|
||||
}
|
||||
return ConditionOutcome
|
||||
.noMatch(message.didNotFind("provided public key").atAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class JwkCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("OAuth JWK Condition");
|
||||
Environment environment = context.getEnvironment();
|
||||
String keyUri = environment
|
||||
.getProperty("security.oauth2.resource.jwk.key-set-uri");
|
||||
if (StringUtils.hasText(keyUri)) {
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("provided jwk key set URI"));
|
||||
}
|
||||
return ConditionOutcome
|
||||
.noMatch(message.didNotFind("key jwk set URI not provided").atAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class NotTokenInfoCondition extends SpringBootCondition {
|
||||
|
||||
private TokenInfoCondition tokenInfoCondition = new TokenInfoCondition();
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
return ConditionOutcome
|
||||
.inverse(this.tokenInfoCondition.getMatchOutcome(context, metadata));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class RemoteTokenCondition extends NoneNestedConditions {
|
||||
|
||||
RemoteTokenCondition() {
|
||||
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
}
|
||||
|
||||
@Conditional(JwtTokenCondition.class)
|
||||
static class HasJwtConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Conditional(JwkCondition.class)
|
||||
static class HasJwkConfiguration {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class AcceptJsonRequestInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class AcceptJsonRequestEnhancer implements RequestEnhancer {
|
||||
|
||||
@Override
|
||||
public void enhance(AccessTokenRequest request,
|
||||
OAuth2ProtectedResourceDetails resource,
|
||||
MultiValueMap<String, String> form, HttpHeaders headers) {
|
||||
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Request;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.social.connect.Connection;
|
||||
import org.springframework.social.connect.UserProfile;
|
||||
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
|
||||
import org.springframework.social.oauth2.AccessGrant;
|
||||
|
||||
/**
|
||||
* {@link ResourceServerTokenServices} backed by Spring Social.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class SpringSocialTokenServices implements ResourceServerTokenServices {
|
||||
|
||||
private final OAuth2ConnectionFactory<?> connectionFactory;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
public SpringSocialTokenServices(OAuth2ConnectionFactory<?> connectionFactory,
|
||||
String clientId) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2Authentication loadAuthentication(String accessToken)
|
||||
throws AuthenticationException, InvalidTokenException {
|
||||
AccessGrant accessGrant = new AccessGrant(accessToken);
|
||||
Connection<?> connection = this.connectionFactory.createConnection(accessGrant);
|
||||
UserProfile user = connection.fetchUserProfile();
|
||||
return extractAuthentication(user);
|
||||
}
|
||||
|
||||
private OAuth2Authentication extractAuthentication(UserProfile user) {
|
||||
String principal = user.getUsername();
|
||||
List<GrantedAuthority> authorities = AuthorityUtils
|
||||
.commaSeparatedStringToAuthorityList("ROLE_USER");
|
||||
OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null,
|
||||
null, null, null, null);
|
||||
return new OAuth2Authentication(request,
|
||||
new UsernamePasswordAuthenticationToken(principal, "N/A", authorities));
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken readAccessToken(String accessToken) {
|
||||
throw new UnsupportedOperationException("Not supported: read access token");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
|
||||
/**
|
||||
* Callback for customizing the rest template used to fetch user details if authentication
|
||||
* is done via OAuth2 access tokens. The default should be fine for most providers, but
|
||||
* occasionally you might need to add additional interceptors, or change the request
|
||||
* authenticator (which is how the token gets attached to outgoing requests). The rest
|
||||
* template that is being customized here is <i>only</i> used internally to carry out
|
||||
* authentication (in the SSO or Resource Server use cases).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface UserInfoRestTemplateCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the rest template before it is initialized.
|
||||
* @param template the rest template
|
||||
*/
|
||||
void customize(OAuth2RestTemplate template);
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
|
||||
/**
|
||||
* Factory used to create the {@link OAuth2RestTemplate} used for extracting user info
|
||||
* during authentication if none is available.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface UserInfoRestTemplateFactory {
|
||||
|
||||
/**
|
||||
* Return the {@link OAuth2RestTemplate} used for extracting user info during
|
||||
* authentication if none is available.
|
||||
* @return the OAuth2RestTemplate used for authentication
|
||||
*/
|
||||
OAuth2RestTemplate getUserInfoRestTemplate();
|
||||
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestOperations;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
|
||||
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Request;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ResourceServerTokenServices} that uses a user info REST service.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class UserInfoTokenServices implements ResourceServerTokenServices {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final String userInfoEndpointUrl;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
private OAuth2RestOperations restTemplate;
|
||||
|
||||
private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;
|
||||
|
||||
private AuthoritiesExtractor authoritiesExtractor = new FixedAuthoritiesExtractor();
|
||||
|
||||
private PrincipalExtractor principalExtractor = new FixedPrincipalExtractor();
|
||||
|
||||
public UserInfoTokenServices(String userInfoEndpointUrl, String clientId) {
|
||||
this.userInfoEndpointUrl = userInfoEndpointUrl;
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public void setTokenType(String tokenType) {
|
||||
this.tokenType = tokenType;
|
||||
}
|
||||
|
||||
public void setRestTemplate(OAuth2RestOperations restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
public void setAuthoritiesExtractor(AuthoritiesExtractor authoritiesExtractor) {
|
||||
Assert.notNull(authoritiesExtractor, "AuthoritiesExtractor must not be null");
|
||||
this.authoritiesExtractor = authoritiesExtractor;
|
||||
}
|
||||
|
||||
public void setPrincipalExtractor(PrincipalExtractor principalExtractor) {
|
||||
Assert.notNull(principalExtractor, "PrincipalExtractor must not be null");
|
||||
this.principalExtractor = principalExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2Authentication loadAuthentication(String accessToken)
|
||||
throws AuthenticationException, InvalidTokenException {
|
||||
Map<String, Object> map = getMap(this.userInfoEndpointUrl, accessToken);
|
||||
if (map.containsKey("error")) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("userinfo returned error: " + map.get("error"));
|
||||
}
|
||||
throw new InvalidTokenException(accessToken);
|
||||
}
|
||||
return extractAuthentication(map);
|
||||
}
|
||||
|
||||
private OAuth2Authentication extractAuthentication(Map<String, Object> map) {
|
||||
Object principal = getPrincipal(map);
|
||||
List<GrantedAuthority> authorities = this.authoritiesExtractor
|
||||
.extractAuthorities(map);
|
||||
OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null,
|
||||
null, null, null, null);
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||
principal, "N/A", authorities);
|
||||
token.setDetails(map);
|
||||
return new OAuth2Authentication(request, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the principal that should be used for the token. The default implementation
|
||||
* delegates to the {@link PrincipalExtractor}.
|
||||
* @param map the source map
|
||||
* @return the principal or {@literal "unknown"}
|
||||
*/
|
||||
protected Object getPrincipal(Map<String, Object> map) {
|
||||
Object principal = this.principalExtractor.extractPrincipal(map);
|
||||
return (principal == null ? "unknown" : principal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken readAccessToken(String accessToken) {
|
||||
throw new UnsupportedOperationException("Not supported: read access token");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
private Map<String, Object> getMap(String path, String accessToken) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Getting user info from: " + path);
|
||||
}
|
||||
try {
|
||||
OAuth2RestOperations restTemplate = this.restTemplate;
|
||||
if (restTemplate == null) {
|
||||
BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
|
||||
resource.setClientId(this.clientId);
|
||||
restTemplate = new OAuth2RestTemplate(resource);
|
||||
}
|
||||
OAuth2AccessToken existingToken = restTemplate.getOAuth2ClientContext()
|
||||
.getAccessToken();
|
||||
if (existingToken == null || !accessToken.equals(existingToken.getValue())) {
|
||||
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(
|
||||
accessToken);
|
||||
token.setTokenType(this.tokenType);
|
||||
restTemplate.getOAuth2ClientContext().setAccessToken(token);
|
||||
}
|
||||
return restTemplate.getForEntity(path, Map.class).getBody();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
this.logger.warn("Could not fetch user details: " + ex.getClass() + ", "
|
||||
+ ex.getMessage());
|
||||
return Collections.<String, Object>singletonMap("error",
|
||||
"Could not fetch user details");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -895,25 +895,6 @@
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "security.oauth2.resource.filter-order",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "The order of the filter chain used to authenticate tokens. Default puts it after\n the actuator endpoints and before the default HTTP basic filter chain (catchall).",
|
||||
"defaultValue": 0,
|
||||
"deprecation": {
|
||||
"reason": "The security auto-configuration does no longer provide several security configurations. Their ordering is now explicit in your own security configuration.",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "security.oauth2.sso.filter-order",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "Filter order to apply if not providing an explicit WebSecurityConfigurerAdapter (in\n which case the order can be provided there instead).",
|
||||
"deprecation": {
|
||||
"reason": "The security auto-configuration does no longer provide several security configurations. Their ordering is now explicit in your own security configuration.",
|
||||
"level": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "security.require-ssl",
|
||||
"type": "java.lang.Boolean",
|
||||
|
||||
@@ -98,7 +98,6 @@ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
|
||||
|
||||
Reference in New Issue
Block a user