Rationalize some features and merge in customizers from Spring Cloud

This commit is contained in:
Dave Syer
2015-05-22 16:32:47 +01:00
parent 5468949a55
commit af320b49bf
14 changed files with 503 additions and 162 deletions

View File

@@ -15,9 +15,6 @@
*/
package org.springframework.boot.autoconfigure.security.oauth2.client;
import java.io.IOException;
import java.util.Arrays;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@@ -27,6 +24,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.oauth2.ClientCredentialsProperties;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -35,25 +36,22 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.http.HttpHeaders;
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.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
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.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
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.util.MultiValueMap;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
/**
* @author Dave Syer
@@ -61,84 +59,119 @@ import org.springframework.util.MultiValueMap;
*/
@Configuration
@ConditionalOnClass(EnableOAuth2Client.class)
@ConditionalOnBean(OAuth2ClientConfiguration.class)
@ConditionalOnExpression("'${spring.oauth2.client.clientId:}'!=''")
public class SpringSecurityOAuth2ClientConfiguration {
private static final Log logger = LogFactory
.getLog(SpringSecurityOAuth2ClientConfiguration.class);
@Autowired
private ClientCredentialsProperties credentials;
@PostConstruct
public void init() {
String prefix = "spring.oauth2.client";
boolean defaultSecret = this.credentials.isDefaultSecret();
logger.info(String.format(
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n",
prefix, this.credentials.getClientId(), prefix,
defaultSecret ? this.credentials.getClientSecret() : "****"));
}
@Bean
@Primary
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails details) {
OAuth2RestTemplate template = new OAuth2RestTemplate(details, oauth2ClientContext);
return template;
}
@Configuration
public static class ClientAuthenticationFilterConfiguration {
@Resource
@Qualifier("accessTokenRequest")
private AccessTokenRequest accessTokenRequest;
@Autowired
private ClientCredentialsProperties credentials;
@PostConstruct
public void init() {
String prefix = "spring.oauth2.client";
boolean defaultSecret = this.credentials.isDefaultSecret();
logger.info(String.format(
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n",
prefix, this.credentials.getClientId(), prefix,
defaultSecret ? this.credentials.getClientSecret() : "****"));
}
protected abstract static class BaseConfiguration {
@Bean
@ConfigurationProperties("spring.oauth2.client")
@Primary
public AuthorizationCodeResourceDetails authorizationCodeResourceDetails() {
public AuthorizationCodeResourceDetails oauth2RemoteResource() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setClientSecret(this.credentials.getClientSecret());
details.setClientId(this.credentials.getClientId());
return details;
}
}
@Configuration
@ConditionalOnNotWebApplication
protected static class SingletonScopedConfiguration {
@Bean
@ConfigurationProperties("spring.oauth2.client")
@Primary
public ClientCredentialsResourceDetails oauth2RemoteResource() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
return details;
}
@Bean
public OAuth2ClientContext oauth2ClientContext() {
return new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest());
}
}
@Configuration
@ConditionalOnBean(OAuth2ClientConfiguration.class)
@ConditionalOnWebApplication
protected static class SessionScopedConfiguration extends BaseConfiguration {
@Resource
@Qualifier("accessTokenRequest")
protected AccessTokenRequest accessTokenRequest;
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2ClientContext oauth2ClientContext() {
return new DefaultOAuth2ClientContext(accessTokenRequest);
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(0);
registration.setOrder(-100);
return registration;
}
@Bean
public OAuth2RestOperations authorizationCodeRestTemplate(
AuthorizationCodeResourceDetails oauth2RemoteResource) {
OAuth2RestTemplate template = new OAuth2RestTemplate(oauth2RemoteResource,
oauth2ClientContext());
template.setInterceptors(Arrays
.<ClientHttpRequestInterceptor> asList(new 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);
}
}));
AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
accessTokenProvider.setTokenRequestEnhancer(new RequestEnhancer() {
@Override
public void enhance(AccessTokenRequest request,
OAuth2ProtectedResourceDetails resource,
MultiValueMap<String, String> form, HttpHeaders headers) {
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
}
});
template.setAccessTokenProvider(accessTokenProvider);
return template;
}
}
/*
* 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 <code>@EnableOAuth2Client</code>
*/
@Configuration
@ConditionalOnMissingBean(OAuth2ClientConfiguration.class)
@ConditionalOnWebApplication
protected static class RequestScopedConfiguration extends BaseConfiguration {
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2ClientContext oauth2ClientContext() {
return new DefaultOAuth2ClientContext(this.accessTokenRequest);
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;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2013-2015 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;
public interface JwtAccessTokenConverterConfigurer {
void configure(JwtAccessTokenConverter converter);
}

View File

@@ -21,6 +21,7 @@ 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.Errors;
@@ -66,6 +67,11 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
*/
private boolean preferTokenInfo = true;
/**
* The token type to send when using the userInfoUri.
*/
private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;
private Jwt jwt = new Jwt();
public ResourceServerProperties() {
@@ -126,6 +132,14 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
this.preferTokenInfo = preferTokenInfo;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public Jwt getJwt() {
return this.jwt;
}

View File

@@ -15,32 +15,48 @@
*/
package org.springframework.boot.autoconfigure.security.oauth2.resource;
import java.io.IOException;
import java.util.Arrays;
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.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.SpringBootCondition;
import org.springframework.boot.autoconfigure.security.oauth2.ClientCredentialsProperties;
import org.springframework.boot.autoconfigure.security.oauth2.client.SpringSecurityOAuth2ClientConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.SpringSecurityOAuth2ClientConfiguration.ClientAuthenticationFilterConfiguration;
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.core.OrderComparator;
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.crypto.codec.Base64;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
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.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
@@ -49,6 +65,7 @@ import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenCo
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.social.connect.ConnectionFactoryLocator;
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
@@ -64,29 +81,88 @@ public class ResourceServerTokenServicesConfiguration {
private static final Log logger = LogFactory
.getLog(ResourceServerTokenServicesConfiguration.class);
@Configuration
protected static class UserInfoRestTemplateConfiguration {
private static final AuthorizationCodeResourceDetails DEFAULT_RESOURCE_DETAILS = new AuthorizationCodeResourceDetails();
static {
DEFAULT_RESOURCE_DETAILS.setClientId("<N/A>");
DEFAULT_RESOURCE_DETAILS
.setUserAuthorizationUri("Not a URI because there is no client");
DEFAULT_RESOURCE_DETAILS
.setAccessTokenUri("Not a URI because there is no client");
}
@Autowired(required = false)
private List<UserInfoRestTemplateCustomizer> customizers = Collections
.emptyList();
@Autowired(required = false)
private OAuth2ProtectedResourceDetails details;
@Autowired(required = false)
private OAuth2ClientContext oauth2ClientContext;
@Bean(name = "userInfoRestTemplate")
public OAuth2RestTemplate userInfoRestTemplate() {
OAuth2RestTemplate template;
if (details == null) {
details = DEFAULT_RESOURCE_DETAILS;
}
if (oauth2ClientContext == null) {
template = new OAuth2RestTemplate(details);
}
else {
template = new OAuth2RestTemplate(details, oauth2ClientContext);
}
template.setInterceptors(Arrays
.<ClientHttpRequestInterceptor> asList(new 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);
}
}));
AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
accessTokenProvider.setTokenRequestEnhancer(new RequestEnhancer() {
@Override
public void enhance(AccessTokenRequest request,
OAuth2ProtectedResourceDetails resource,
MultiValueMap<String, String> form, HttpHeaders headers) {
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
}
});
template.setAccessTokenProvider(accessTokenProvider);
OrderComparator.sort(customizers);
for (UserInfoRestTemplateCustomizer customizer : customizers) {
customizer.customize(template);
}
return template;
}
}
@Configuration
@Conditional(NotJwtToken.class)
@EnableOAuth2Client
@Import(ClientAuthenticationFilterConfiguration.class)
protected static class RemoteTokenServicesConfiguration {
@Configuration
@Import(SpringSecurityOAuth2ClientConfiguration.class)
@Conditional(TokenInfo.class)
protected static class TokenInfoServicesConfiguration {
@Autowired
private ResourceServerProperties resource;
@Autowired
private AuthorizationCodeResourceDetails client;
@Bean
public ResourceServerTokenServices remoteTokenServices() {
RemoteTokenServices services = new RemoteTokenServices();
services.setCheckTokenEndpointUrl(this.resource.getTokenInfoUri());
services.setClientId(this.client.getClientId());
services.setClientSecret(this.client.getClientSecret());
services.setClientId(this.resource.getClientId());
services.setClientSecret(this.resource.getClientSecret());
return services;
}
@@ -100,21 +176,19 @@ public class ResourceServerTokenServicesConfiguration {
@Autowired
private ResourceServerProperties sso;
@Autowired
private ClientCredentialsProperties client;
@Autowired(required = false)
private OAuth2ConnectionFactory<?> connectionFactory;
@Autowired(required = false)
private Map<String, OAuth2RestOperations> resources = Collections.emptyMap();
@Qualifier("userInfoRestTemplate")
private OAuth2RestOperations restTemplate;
@Bean
@ConditionalOnBean(ConnectionFactoryLocator.class)
@ConditionalOnMissingBean(ResourceServerTokenServices.class)
public SpringSocialTokenServices socialTokenServices() {
return new SpringSocialTokenServices(this.connectionFactory,
this.client.getClientId());
this.sso.getClientId());
}
@Bean
@@ -122,33 +196,33 @@ public class ResourceServerTokenServicesConfiguration {
ResourceServerTokenServices.class })
public ResourceServerTokenServices userInfoTokenServices() {
UserInfoTokenServices services = new UserInfoTokenServices(
this.sso.getUserInfoUri(), this.client.getClientId());
services.setResources(this.resources);
this.sso.getUserInfoUri(), this.sso.getClientId());
services.setTokenType(sso.getTokenType());
services.setRestTemplate(restTemplate);
return services;
}
}
@Configuration
@ConditionalOnMissingClass(name = "org.springframework.social.connect.support.OAuth2ConnectionFactory")
@ConditionalOnMissingClass("org.springframework.social.connect.support.OAuth2ConnectionFactory")
@Conditional(NotTokenInfo.class)
protected static class UserInfoTokenServicesConfiguration {
@Autowired
private ResourceServerProperties sso;
@Autowired
private ClientCredentialsProperties client;
@Autowired(required = false)
private Map<String, OAuth2RestOperations> resources = Collections.emptyMap();
@Qualifier("userInfoRestTemplate")
private OAuth2RestOperations restTemplate;
@Bean
@ConditionalOnMissingBean(ResourceServerTokenServices.class)
public ResourceServerTokenServices userInfoTokenServices() {
UserInfoTokenServices services = new UserInfoTokenServices(
this.sso.getUserInfoUri(), this.client.getClientId());
services.setResources(this.resources);
this.sso.getUserInfoUri(), this.sso.getClientId());
services.setRestTemplate(restTemplate);
services.setTokenType(sso.getTokenType());
return services;
}
@@ -160,9 +234,15 @@ public class ResourceServerTokenServicesConfiguration {
@Conditional(JwtToken.class)
protected static class JwtTokenServicesConfiguration {
private RestTemplate keyUriRestTemplate = new RestTemplate();
@Autowired
private ResourceServerProperties resource;
@Autowired(required = false)
private List<JwtAccessTokenConverterConfigurer> configurers = Collections
.emptyList();
@Bean
@ConditionalOnMissingBean(ResourceServerTokenServices.class)
public ResourceServerTokenServices jwtTokenServices() {
@@ -182,22 +262,34 @@ public class ResourceServerTokenServicesConfiguration {
String keyValue = this.resource.getJwt().getKeyValue();
if (!StringUtils.hasText(keyValue)) {
try {
keyValue = (String) new RestTemplate().getForObject(
this.resource.getJwt().getKeyUri(), Map.class).get("value");
HttpHeaders headers = new HttpHeaders();
if (resource.getClientId() != null
&& resource.getClientSecret() != null) {
byte[] token = Base64
.encode((resource.getClientId() + ":" + resource
.getClientSecret()).getBytes());
headers.add("Authorization", "Basic " + new String(token));
}
HttpEntity<Void> requestEntity = new HttpEntity<Void>(headers);
keyValue = (String) keyUriRestTemplate
.exchange(resource.getJwt().getKeyUri(), HttpMethod.GET,
requestEntity, Map.class).getBody().get("value");
}
catch (ResourceAccessException e) {
// ignore
logger.warn("Failed to fetch token key (you may need to refresh when the auth server is back)");
}
}
else {
if (StringUtils.hasText(keyValue) && !keyValue.startsWith("-----BEGIN")) {
converter.setSigningKey(keyValue);
}
if (StringUtils.hasText(keyValue) && !keyValue.startsWith("-----BEGIN")) {
converter.setSigningKey(keyValue);
}
if (keyValue != null) {
converter.setVerifierKey(keyValue);
}
AnnotationAwareOrderComparator.sort(configurers);
for (JwtAccessTokenConverterConfigurer configurer : configurers) {
configurer.configure(converter);
}
return converter;
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2014-2015 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
*
*/
public interface UserInfoRestTemplateCustomizer {
/**
* Customize the rest template before it is initialized.
*
* @param template the rest template
*/
void customize(OAuth2RestTemplate template);
}

View File

@@ -15,11 +15,7 @@
*/
package org.springframework.boot.autoconfigure.security.oauth2.resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -44,22 +40,21 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
private String clientId;
private Collection<OAuth2RestOperations> resources = Collections.emptySet();
private OAuth2RestOperations restTemplate;
private String tokenType = DefaultOAuth2AccessToken.BEARER_TYPE;
public UserInfoTokenServices(String userInfoEndpointUrl, String clientId) {
this.userInfoEndpointUrl = userInfoEndpointUrl;
this.clientId = clientId;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public void setResources(Map<String, OAuth2RestOperations> resources) {
this.resources = new ArrayList<OAuth2RestOperations>();
for (Entry<String, OAuth2RestOperations> key : resources.entrySet()) {
OAuth2RestOperations value = key.getValue();
String clientIdForTemplate = value.getResource().getClientId();
if (clientIdForTemplate!=null && clientIdForTemplate.equals(clientId)) {
this.resources.add(value);
}
}
public void setRestTemplate(OAuth2RestOperations restTemplate) {
this.restTemplate = restTemplate;
}
@Override
@@ -87,8 +82,8 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
}
private Object getPrincipal(Map<String, Object> map) {
String[] keys = new String[] { "user", "username", "userid", "user_id", "login",
"id" };
String[] keys = new String[] { "user", "username", "userid", "user_id", "login",
"id", "name" };
for (String key : keys) {
if (map.containsKey(key)) {
return map.get(key);
@@ -104,23 +99,15 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
private Map<String, Object> getMap(String path, String accessToken) {
logger.info("Getting user info from: " + path);
OAuth2RestOperations restTemplate = null;
for (OAuth2RestOperations candidate : resources) {
try {
if (accessToken.equals(candidate.getAccessToken().getValue())) {
restTemplate = candidate;
}
}
catch (Exception e) {
}
}
OAuth2RestOperations restTemplate = this.restTemplate;
if (restTemplate == null) {
BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
resource.setClientId(clientId);
restTemplate = new OAuth2RestTemplate(resource);
restTemplate.getOAuth2ClientContext().setAccessToken(
new DefaultOAuth2AccessToken(accessToken));
}
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken);
token.setTokenType(tokenType);
restTemplate.getOAuth2ClientContext().setAccessToken(token);
@SuppressWarnings("rawtypes")
Map map = restTemplate.getForEntity(path, Map.class).getBody();
@SuppressWarnings("unchecked")

View File

@@ -51,8 +51,8 @@ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.SpringSecurityOAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\

View File

@@ -19,7 +19,7 @@ package org.springframework.boot.autoconfigure;
import org.springframework.boot.test.AbstractConfigurationClassTests;
/**
* Tests for the autoconfigure module's @Configuration classes
* Tests for the autoconfigure module's <code>@Configuration</code> classes
* @author Andy Wilkinson
*/
public class AutoConfigureConfigurationClassTests extends AbstractConfigurationClassTests {

View File

@@ -16,12 +16,15 @@
package org.springframework.boot.autoconfigure.security.oauth2;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
@@ -74,7 +77,6 @@ import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserAp
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService;
import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
@@ -90,10 +92,6 @@ import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Verify Spring Security OAuth2 auto-configuration secures end points properly, accepts
* environmental overrides, and also backs off in the presence of other

View File

@@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -58,14 +57,14 @@ public class UserInfoTokenServicesTests {
@Test
public void sunnyDay() {
services.setResources(Collections.singletonMap("foo", template));
services.setRestTemplate(template);
assertEquals("unknown", services.loadAuthentication("FOO").getName());
}
@Test
public void userId() {
map.put("userid", "spencer");
services.setResources(Collections.singletonMap("foo", template));
services.setRestTemplate(template);
assertEquals("spencer", services.loadAuthentication("FOO").getName());
}