Rationalize some features and merge in customizers from Spring Cloud
This commit is contained in:
parent
5468949a55
commit
af320b49bf
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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,\
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@
|
|||
<spring-social-linkedin.version>1.0.1.RELEASE</spring-social-linkedin.version>
|
||||
<spring-social-twitter.version>1.1.0.RELEASE</spring-social-twitter.version>
|
||||
<spring-security.version>3.2.5.RELEASE</spring-security.version>
|
||||
<spring-security-oauth2.version>2.0.5.RELEASE</spring-security-oauth2.version>
|
||||
<spring-security-oauth2.version>2.0.7.RELEASE</spring-security-oauth2.version>
|
||||
<spring-security-jwt.version>1.0.2.RELEASE</spring-security-jwt.version>
|
||||
<spring-ws.version>2.2.1.RELEASE</spring-ws.version>
|
||||
<statsd-client.version>3.1.0</statsd-client.version>
|
||||
|
|
|
|||
|
|
@ -1460,6 +1460,162 @@ All of the above can be switched on and off or modified using external propertie
|
|||
features add a `@Bean` of type `WebSecurityConfigurerAdapter` with
|
||||
`@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)`.
|
||||
|
||||
=== OAuth2
|
||||
|
||||
If you have `spring-security-oauth2` on your classpath you can take advantage of some
|
||||
autoconfiguration to make it easy to set up Authorization or Resource Server features by
|
||||
configuring some property values in the `Environment`.
|
||||
|
||||
==== Authorization Server
|
||||
|
||||
To create an Authorization Server and grant access tokens you need to
|
||||
`@EnableAuthorizationServer` and provide
|
||||
`spring.oauth2.client.[clientId,clientSecret]`. The client will be
|
||||
registered for you in an in-memory repository. To switch off the
|
||||
autoconfiguration and configure the Authorization Server features
|
||||
yourself just add a `@Bean` of type
|
||||
`AuthorizationServerConfigurer`. Having done that you will be able to
|
||||
usethe client credentials to create an access token, e.g.
|
||||
|
||||
----
|
||||
$ curl client:secret@localhost:8080/oauth/token -d grant_type=password -d username=user -d password=pwd
|
||||
----
|
||||
|
||||
The basic auth credentials for the `/token` endpoint are the client id
|
||||
and secret, and the user credentials are the normal Spring Security
|
||||
user details (which default in Spring Boot to "user" and a random
|
||||
password).
|
||||
|
||||
==== Resource Server
|
||||
|
||||
To use the access token you need a Resource Server (which can be the
|
||||
same as the Authorization Server). Creating a Resource Server is easy:
|
||||
just add `@EnableResourceServer` and provide some configuration to
|
||||
allow the server to decode access tokens. If your app is also an
|
||||
Authorization Server it already knows how to decode tokens, so there
|
||||
is nothing else to do. If your app is a standalone service then you
|
||||
need to give it some more configuration. Here are the options, one of
|
||||
the following:
|
||||
|
||||
* `spring.oauth2.resource.userInfoUri` to use the "/me" resource
|
||||
(e.g. "https://uaa.run.pivotal.io/userinfo" on PWS), or
|
||||
|
||||
* `spring.oauth2.resource.tokenInfoUri` to use the token decoding endpoint
|
||||
(e.g. "https://uaa.run.pivotal.io/check_token" on PWS).
|
||||
|
||||
If you specify both the `userInfoUri` and the `tokenInfoUri` then
|
||||
you can set a flag to say that one is preferred over the other
|
||||
(`preferTokenInfo=true` is the default).
|
||||
|
||||
Alternatively (instead of `userInfoUri` or `tokenInfoUri`) if the
|
||||
tokens are JWTs you can configure a
|
||||
`spring.oauth2.resource.jwt.keyValue` to decode them locally,
|
||||
where the key is a verification key. The verification key value is
|
||||
either a symmetric secret or PEM-encoded RSA public key. If you don't
|
||||
have the key and it's public you can provide a URI where it can be
|
||||
downloaded (as a JSON object with a "value" field) with
|
||||
`spring.oauth2.resource.jwt.keyUri`. E.g. on PWS:
|
||||
|
||||
----
|
||||
$ curl https://uaa.run.pivotal.io/token_key
|
||||
{"alg":"SHA256withRSA","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"}
|
||||
----
|
||||
|
||||
WARNING: If you use the `spring.oauth2.resource.jwt.keyUri` the
|
||||
authorization server needs to be running when your application starts
|
||||
up. It will log a warning if it can't find the key, and tell you what
|
||||
to do to fix it.
|
||||
|
||||
=== Token Type in User Info
|
||||
|
||||
Google (and certain other 3rd party identity providers) is more strict
|
||||
about the token type name that is sent in the headers to the user info
|
||||
endpoint. The default is "Bearer" which suits most providers and
|
||||
matches the spec, but if you need to change it you can set
|
||||
`spring.oauth2.resource.tokenType`.
|
||||
|
||||
=== Customizing the User Info RestTemplate
|
||||
|
||||
If you have a `userInfoUri`, the Resource Server features use an
|
||||
`OAuth2RestTemplate` internally to fetch user details for
|
||||
authentication. This is provided as a qualified `@Bean` with id
|
||||
"userInfoRestTemplate", but you shouldn't need to know that to just
|
||||
use it. 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). To add a customization just create a bean of type
|
||||
`UserInfoRestTemplateCustomizer` - it has a single method that will be
|
||||
called after the bean is created but before it is initialized. The
|
||||
rest template that is being customized here is _only_ used internally
|
||||
to carry out authentication.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
To set an RSA key value in YAML use the "pipe" continuation
|
||||
marker to split it over multiple lines ("|") and remember to indent
|
||||
the key value (it's a standard YAML language feature). Example:
|
||||
|
||||
[source,yaml,indent=0]
|
||||
----
|
||||
oauth2:
|
||||
resource:
|
||||
jwt:
|
||||
keyValue: |
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC...
|
||||
-----END PUBLIC KEY-----
|
||||
----
|
||||
====
|
||||
|
||||
==== Client
|
||||
|
||||
To make your webapp into an OAuth2 client you can simply
|
||||
`@EnableOAuth2Client` and Spring Boot will create an
|
||||
`OAuth2RestTemplate` for you to autowire. It uses the
|
||||
`spring.oauth2.client.*` as credentials (the same as you might be
|
||||
using in the Authorization Server), but in addition it will need to
|
||||
know the authorization and token URIs in the Authorization Server. For
|
||||
example:
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
spring:
|
||||
oauth2:
|
||||
client:
|
||||
clientId: bd1c0a783ccdd1c9b9e4
|
||||
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
|
||||
accessTokenUri: https://github.com/login/oauth/access_token
|
||||
userAuthorizationUri: https://github.com/login/oauth/authorize
|
||||
clientAuthenticationScheme: form
|
||||
resource:
|
||||
userInfoUri: https://api.github.com/user
|
||||
preferTokenInfo: false
|
||||
----
|
||||
|
||||
An app with this configuration will redirect to github for
|
||||
authorization if you attempt to use the `OAuth2RestTemplate`. If you
|
||||
are already signed into github you won't even notice that it has
|
||||
authenticated. These specific credentials will only work if your app
|
||||
is running on port 8080 (register your own client app in Github or
|
||||
other provider for more flexibility).
|
||||
|
||||
To limit the scope that the client asks for when it obtains an access token
|
||||
you can set `spring.oauth2.client.scope` (comma separated or an array in YAML). By
|
||||
default the scope is empty and it is up to to Authorization Server to
|
||||
decide what the defaults should be, usually depending on the settings in
|
||||
the client registration that it holds.
|
||||
|
||||
NOTE: There is also a setting for
|
||||
`spring.oauth2.client.clientAuthenticationScheme` which defaults to
|
||||
"header" (but you might need to set it to "form" if, like Github for
|
||||
instance, your OAuth2 provider doesn't like header authentication). In
|
||||
fact, the `spring.oauth2.client.*` properties are bound to an instance
|
||||
of `AuthorizationCodeResourceDetails` so all its properties can be
|
||||
specified.
|
||||
|
||||
=== Actuator Security
|
||||
|
||||
If the Actuator is also in use, you will find:
|
||||
|
||||
* The management endpoints are secure even if the application endpoints are unsecure.
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@
|
|||
package sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
|
||||
|
|
@ -95,9 +93,7 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E
|
|||
*/
|
||||
// @formatter:on
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootApplication
|
||||
@EnableAuthorizationServer
|
||||
@EnableResourceServer
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
package sample;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
|
|
@ -21,14 +30,6 @@ import org.springframework.web.context.WebApplicationContext;
|
|||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
|
||||
|
||||
/**
|
||||
* Series of automated integration tests to verify proper behavior of auto-configured,
|
||||
* OAuth2-secured system
|
||||
|
|
@ -103,20 +104,20 @@ public class ApplicationTests {
|
|||
@Test
|
||||
public void useAppSecretsPlusUserAccountToGetBearerToken() throws Exception {
|
||||
|
||||
// @formatter:off
|
||||
MvcResult result = this.mvc
|
||||
.perform(
|
||||
get("/oauth/token").//
|
||||
post("/oauth/token").
|
||||
header("Authorization",
|
||||
"Basic "
|
||||
+ new String(Base64.encode("foo:bar"
|
||||
.getBytes()))).//
|
||||
param("grant_type", "password").//
|
||||
param("scope", "read").//
|
||||
param("username", "greg").//
|
||||
param("password", "turnquist")).//
|
||||
andExpect(status().isOk()).//
|
||||
andDo(print()).//
|
||||
"Basic " + new String(Base64.encode("foo:bar".getBytes()))).
|
||||
param("grant_type", "password").
|
||||
param("scope", "read").
|
||||
param("username", "greg").
|
||||
param("password", "turnquist")).
|
||||
andExpect(status().isOk()).
|
||||
andDo(print()).
|
||||
andReturn();
|
||||
// @formatter:on
|
||||
|
||||
Object accessToken = this.objectMapper.readValue(
|
||||
result.getResponse().getContentAsString(), Map.class).get("access_token");
|
||||
|
|
|
|||
Loading…
Reference in New Issue