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:
Andy Wilkinson 2017-09-21 10:30:34 +01:00
parent a0fee6fe3b
commit e9147c2f20
79 changed files with 2 additions and 6877 deletions

View File

@ -522,16 +522,6 @@
<artifactId>spring-security-data</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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 {
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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 {
}
}
}

View File

@ -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();
}
}
}

View File

@ -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));
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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));
}
}
}

View File

@ -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");
}
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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");
}
}
}

View File

@ -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",

View File

@ -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,\

View File

@ -1,683 +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.net.URI;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.authserver.OAuth2AuthorizationServerConfiguration;
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.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource;
import org.springframework.security.access.annotation.SecuredAnnotationSecurityMetadataSource;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource;
import org.springframework.security.access.method.MethodSecurityMetadataSource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserApprovalHandler;
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;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Verify Spring Security OAuth2 auto-configuration secures end points properly, accepts
* environmental overrides, and also backs off in the presence of other
* resource/authorization components.
*
* @author Greg Turnquist
* @author Dave Syer
*/
public class OAuth2AutoConfigurationTests {
private static final Class<?> RESOURCE_SERVER_CONFIG = OAuth2ResourceServerConfiguration.class;
private static final Class<?> AUTHORIZATION_SERVER_CONFIG = OAuth2AuthorizationServerConfiguration.class;
private AnnotationConfigServletWebServerApplicationContext context;
@Test
public void testDefaultConfiguration() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(AuthorizationAndResourceServerConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
this.context.getBean(AUTHORIZATION_SERVER_CONFIG);
this.context.getBean(RESOURCE_SERVER_CONFIG);
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(BaseClientDetails.class);
AuthorizationEndpoint endpoint = this.context
.getBean(AuthorizationEndpoint.class);
UserApprovalHandler handler = (UserApprovalHandler) ReflectionTestUtils
.getField(endpoint, "userApprovalHandler");
ClientDetailsService clientDetailsService = this.context
.getBean(ClientDetailsService.class);
ClientDetails clientDetails = clientDetailsService
.loadClientByClientId(config.getClientId());
assertThat(AopUtils.isJdkDynamicProxy(clientDetailsService)).isTrue();
assertThat(AopUtils.getTargetClass(clientDetailsService).getName())
.isEqualTo(InMemoryClientDetailsService.class.getName());
assertThat(handler).isInstanceOf(ApprovalStoreUserApprovalHandler.class);
assertThat(clientDetails).isEqualTo(config);
verifyAuthentication(config);
assertThat(this.context.getBeanNamesForType(OAuth2RestOperations.class))
.isEmpty();
}
@Test
public void methodSecurityExpressionHandlerIsConfiguredWithRoleHierarchyFromTheContext() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(RoleHierarchyConfiguration.class,
AuthorizationAndResourceServerConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
PreInvocationAuthorizationAdvice advice = this.context
.getBean(PreInvocationAuthorizationAdvice.class);
MethodSecurityExpressionHandler expressionHandler = (MethodSecurityExpressionHandler) ReflectionTestUtils
.getField(advice, "expressionHandler");
RoleHierarchy roleHierarchy = (RoleHierarchy) ReflectionTestUtils
.getField(expressionHandler, "roleHierarchy");
assertThat(roleHierarchy).isSameAs(this.context.getBean(RoleHierarchy.class));
}
@Test
public void methodSecurityExpressionHandlerIsConfiguredWithPermissionEvaluatorFromTheContext() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(PermissionEvaluatorConfiguration.class,
AuthorizationAndResourceServerConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
PreInvocationAuthorizationAdvice advice = this.context
.getBean(PreInvocationAuthorizationAdvice.class);
MethodSecurityExpressionHandler expressionHandler = (MethodSecurityExpressionHandler) ReflectionTestUtils
.getField(advice, "expressionHandler");
PermissionEvaluator permissionEvaluator = (PermissionEvaluator) ReflectionTestUtils
.getField(expressionHandler, "permissionEvaluator");
assertThat(permissionEvaluator)
.isSameAs(this.context.getBean(PermissionEvaluator.class));
}
@Test
public void testEnvironmentalOverrides() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues
.of("security.oauth2.client.clientId:myclientid",
"security.oauth2.client.clientSecret:mysecret",
"security.oauth2.client.autoApproveScopes:read,write",
"security.oauth2.client.accessTokenValiditySeconds:40",
"security.oauth2.client.refreshTokenValiditySeconds:80")
.applyTo(this.context);
this.context.register(AuthorizationAndResourceServerConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
ClientDetails config = this.context.getBean(ClientDetails.class);
assertThat(config.getClientId()).isEqualTo("myclientid");
assertThat(config.getClientSecret()).isEqualTo("mysecret");
assertThat(config.isAutoApprove("read")).isTrue();
assertThat(config.isAutoApprove("write")).isTrue();
assertThat(config.isAutoApprove("foo")).isFalse();
assertThat(config.getAccessTokenValiditySeconds()).isEqualTo(40);
assertThat(config.getRefreshTokenValiditySeconds()).isEqualTo(80);
verifyAuthentication(config);
}
@Test
public void testDisablingResourceServer() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(AuthorizationServerConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(0);
assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(1);
}
@Test
public void testClientIsNotResourceServer() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(ClientConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(0);
assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0);
// Scoped target and proxy:
assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2);
}
@Test
public void testCanUseClientCredentials() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(TestSecurityConfiguration.class,
MinimalSecureWebApplication.class);
TestPropertyValues
.of("security.oauth2.client.clientId=client",
"security.oauth2.client.grantType=client_credentials")
.applyTo(this.context);
ConfigurationPropertySources.attach(this.context.getEnvironment());
this.context.refresh();
OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class);
assertThat(bean.getAccessTokenRequest()).isNotNull();
assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1);
assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(1);
}
@Test
public void testCanUseClientCredentialsWithEnableOAuth2Client() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(ClientConfiguration.class,
MinimalSecureWebApplication.class);
TestPropertyValues
.of("security.oauth2.client.clientId=client",
"security.oauth2.client.grantType=client_credentials")
.applyTo(this.context);
ConfigurationPropertySources.attach(this.context.getEnvironment());
this.context.refresh();
// The primary context is fine (not session scoped):
OAuth2ClientContext bean = this.context.getBean(OAuth2ClientContext.class);
assertThat(bean.getAccessTokenRequest()).isNotNull();
assertThat(countBeans(ClientCredentialsResourceDetails.class)).isEqualTo(1);
// Kind of a bug (should ideally be 1), but the cause is in Spring OAuth2 (there
// is no need for the extra session-scoped bean). What this test proves is that
// even if the user screws up and does @EnableOAuth2Client for client credentials,
// it will still just about work (because of the @Primary annotation on the
// Boot-created instance of OAuth2ClientContext).
assertThat(countBeans(OAuth2ClientContext.class)).isEqualTo(2);
}
@Test
public void testClientIsNotAuthCode() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MinimalSecureNonWebApplication.class);
TestPropertyValues.of("security.oauth2.client.clientId=client").applyTo(context);
context.refresh();
assertThat(countBeans(context, ClientCredentialsResourceDetails.class))
.isEqualTo(1);
context.close();
}
@Test
public void testDisablingAuthorizationServer() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(ResourceServerConfiguration.class,
MinimalSecureWebApplication.class);
TestPropertyValues.of("security.oauth2.resource.jwt.keyValue:DEADBEEF")
.applyTo(this.context);
ConfigurationPropertySources.attach(this.context.getEnvironment());
this.context.refresh();
assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1);
assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0);
assertThat(countBeans(UserApprovalHandler.class)).isEqualTo(0);
assertThat(countBeans(DefaultTokenServices.class)).isEqualTo(1);
}
@Test
public void testResourceServerOverride() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(AuthorizationAndResourceServerConfiguration.class,
CustomResourceServer.class, MinimalSecureWebApplication.class);
this.context.refresh();
ClientDetails config = this.context.getBean(ClientDetails.class);
assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(1);
assertThat(countBeans(CustomResourceServer.class)).isEqualTo(1);
assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1);
verifyAuthentication(config);
}
@Test
public void testAuthorizationServerOverride() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues.of("security.oauth2.resourceId:resource-id")
.applyTo(this.context);
this.context.register(AuthorizationAndResourceServerConfiguration.class,
CustomAuthorizationServer.class, MinimalSecureWebApplication.class);
this.context.refresh();
BaseClientDetails config = new BaseClientDetails();
config.setClientId("client");
config.setClientSecret("secret");
config.setResourceIds(Arrays.asList("resource-id"));
config.setAuthorizedGrantTypes(Arrays.asList("password"));
config.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("USER"));
config.setScope(Arrays.asList("read"));
assertThat(countBeans(AUTHORIZATION_SERVER_CONFIG)).isEqualTo(0);
assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1);
verifyAuthentication(config);
}
@Test
public void testDefaultPrePostSecurityAnnotations() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(AuthorizationAndResourceServerConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(ClientDetails.class);
DelegatingMethodSecurityMetadataSource source = this.context
.getBean(DelegatingMethodSecurityMetadataSource.class);
List<MethodSecurityMetadataSource> sources = source
.getMethodSecurityMetadataSources();
assertThat(sources.size()).isEqualTo(1);
assertThat(sources.get(0).getClass().getName())
.isEqualTo(PrePostAnnotationSecurityMetadataSource.class.getName());
verifyAuthentication(config);
}
@Test
public void testClassicSecurityAnnotationOverride() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(SecuredEnabledConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(ClientDetails.class);
DelegatingMethodSecurityMetadataSource source = this.context
.getBean(DelegatingMethodSecurityMetadataSource.class);
List<MethodSecurityMetadataSource> sources = source
.getMethodSecurityMetadataSources();
assertThat(sources.size()).isEqualTo(1);
assertThat(sources.get(0).getClass().getName())
.isEqualTo(SecuredAnnotationSecurityMetadataSource.class.getName());
verifyAuthentication(config, HttpStatus.OK);
}
@Test
public void testJsr250SecurityAnnotationOverride() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(Jsr250EnabledConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(ClientDetails.class);
DelegatingMethodSecurityMetadataSource source = this.context
.getBean(DelegatingMethodSecurityMetadataSource.class);
List<MethodSecurityMetadataSource> sources = source
.getMethodSecurityMetadataSources();
assertThat(sources.size()).isEqualTo(1);
assertThat(sources.get(0).getClass().getName())
.isEqualTo(Jsr250MethodSecurityMetadataSource.class.getName());
verifyAuthentication(config, HttpStatus.OK);
}
@Test
public void testMethodSecurityBackingOff() {
this.context = new AnnotationConfigServletWebServerApplicationContext();
this.context.register(CustomMethodSecurity.class, TestSecurityConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
DelegatingMethodSecurityMetadataSource source = this.context
.getBean(DelegatingMethodSecurityMetadataSource.class);
List<MethodSecurityMetadataSource> sources = source
.getMethodSecurityMetadataSources();
assertThat(sources.size()).isEqualTo(1);
assertThat(sources.get(0).getClass().getName())
.isEqualTo(PrePostAnnotationSecurityMetadataSource.class.getName());
}
@Test
public void resourceServerConditionWhenJwkConfigurationPresentShouldMatch()
throws Exception {
this.context = new AnnotationConfigServletWebServerApplicationContext();
TestPropertyValues
.of("security.oauth2.resource.jwk.key-set-uri:http://my-auth-server/token_keys")
.applyTo(this.context);
this.context.register(ResourceServerConfiguration.class,
MinimalSecureWebApplication.class);
this.context.refresh();
assertThat(countBeans(RESOURCE_SERVER_CONFIG)).isEqualTo(1);
}
/**
* Connect to the oauth service, get a token, and then attempt some operations using
* it.
* @param config the client details.
*/
private void verifyAuthentication(ClientDetails config) {
verifyAuthentication(config, HttpStatus.FORBIDDEN);
}
private void verifyAuthentication(ClientDetails config, HttpStatus finalStatus) {
String baseUrl = "http://localhost:" + this.context.getWebServer().getPort();
TestRestTemplate rest = new TestRestTemplate();
// First, verify the web endpoint can't be reached
assertEndpointUnauthorized(baseUrl, rest);
// Since we can't reach it, need to collect an authorization token
HttpHeaders headers = getHeaders(config);
String url = baseUrl + "/oauth/token";
JsonNode tokenResponse = rest.postForObject(url,
new HttpEntity<>(getBody(), headers), JsonNode.class);
String authorizationToken = tokenResponse.findValue("access_token").asText();
String tokenType = tokenResponse.findValue("token_type").asText();
String scope = tokenResponse.findValues("scope").get(0).toString();
assertThat(tokenType).isEqualTo("bearer");
assertThat(scope).isEqualTo("\"read\"");
// Now we should be able to see that endpoint.
headers.set("Authorization", "BEARER " + authorizationToken);
ResponseEntity<String> securedResponse = rest
.exchange(new RequestEntity<Void>(headers, HttpMethod.GET,
URI.create(baseUrl + "/securedFind")), String.class);
assertThat(securedResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(securedResponse.getBody()).isEqualTo(
"You reached an endpoint " + "secured by Spring Security OAuth2");
ResponseEntity<String> entity = rest.exchange(new RequestEntity<Void>(headers,
HttpMethod.POST, URI.create(baseUrl + "/securedSave")), String.class);
assertThat(entity.getStatusCode()).isEqualTo(finalStatus);
}
private HttpHeaders getHeaders(ClientDetails config) {
HttpHeaders headers = new HttpHeaders();
String token = new String(Base64.getEncoder().encode(
(config.getClientId() + ":" + config.getClientSecret()).getBytes()));
headers.set("Authorization", "Basic " + token);
return headers;
}
private MultiValueMap<String, Object> getBody() {
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.set("grant_type", "password");
body.set("username", "foo");
body.set("password", "bar");
body.set("scope", "read");
return body;
}
private void assertEndpointUnauthorized(String baseUrl, TestRestTemplate rest) {
URI uri = URI.create(baseUrl + "/secured");
ResponseEntity<String> entity = rest
.exchange(new RequestEntity<Void>(HttpMethod.GET, uri), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
private int countBeans(Class<?> type) {
return countBeans(this.context, type);
}
private int countBeans(ApplicationContext context, Class<?> type) {
return context.getBeanNamesForType(type).length;
}
@Configuration
@Import({ UseFreePortEmbeddedContainerConfiguration.class,
SecurityAutoConfiguration.class, DispatcherServletAutoConfiguration.class,
OAuth2AutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class })
protected static class MinimalSecureWebApplication {
}
@Configuration
@Import({ SecurityAutoConfiguration.class, OAuth2AutoConfiguration.class })
protected static class MinimalSecureNonWebApplication {
}
@Configuration
protected static class TestSecurityConfiguration
extends WebSecurityConfigurerAdapter {
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("foo").password("bar").roles("USER");
}
@Bean
TestWebApp testWebApp() {
return new TestWebApp();
}
}
@Configuration
@EnableOAuth2Client
protected static class ClientConfiguration extends TestSecurityConfiguration {
}
@Configuration
@EnableAuthorizationServer
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class AuthorizationAndResourceServerConfiguration
extends TestSecurityConfiguration {
}
@Configuration
@EnableAuthorizationServer
@EnableResourceServer
@EnableGlobalMethodSecurity(securedEnabled = true)
protected static class SecuredEnabledConfiguration extends TestSecurityConfiguration {
}
@Configuration
@EnableAuthorizationServer
@EnableResourceServer
@EnableGlobalMethodSecurity(jsr250Enabled = true)
protected static class Jsr250EnabledConfiguration extends TestSecurityConfiguration {
}
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration
extends TestSecurityConfiguration {
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends TestSecurityConfiguration {
}
@RestController
protected static class TestWebApp {
@GetMapping("/securedFind")
@PreAuthorize("#oauth2.hasScope('read')")
public String secureFind() {
return "You reached an endpoint secured by Spring Security OAuth2";
}
@PostMapping("/securedSave")
@PreAuthorize("#oauth2.hasScope('write')")
public String secureSave() {
return "You reached an endpoint secured by Spring Security OAuth2";
}
}
@Configuration
protected static class UseFreePortEmbeddedContainerConfiguration {
@Bean
TomcatServletWebServerFactory webServerFactory() {
return new TomcatServletWebServerFactory(0);
}
}
@Configuration
@EnableResourceServer
protected static class CustomResourceServer extends ResourceServerConfigurerAdapter {
private final ResourceServerProperties config;
protected CustomResourceServer(ResourceServerProperties config) {
this.config = config;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
if (this.config.getId() != null) {
resources.resourceId(this.config.getId());
}
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and()
.csrf().disable();
}
}
@Configuration
@EnableAuthorizationServer
protected static class CustomAuthorizationServer
extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
protected CustomAuthorizationServer(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
@Bean
public ApprovalStore approvalStore(final TokenStore tokenStore) {
TokenApprovalStore approvalStore = new TokenApprovalStore();
approvalStore.setTokenStore(tokenStore);
return approvalStore;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("client").secret("secret")
.resourceIds("resource-id").authorizedGrantTypes("password")
.authorities("USER").scopes("read")
.redirectUris("http://localhost:8080");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(this.authenticationManager);
}
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class CustomMethodSecurity
extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
@Configuration
protected static class RoleHierarchyConfiguration {
@Bean
public RoleHierarchy roleHierarchy() {
return mock(RoleHierarchy.class);
}
}
@Configuration
protected static class PermissionEvaluatorConfiguration {
@Bean
public PermissionEvaluator permissionEvaluator() {
return mock(PermissionEvaluator.class);
}
}
}

View File

@ -1,137 +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.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.web.servlet.MockServletWebServerFactory;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2RestOperationsConfiguration}.
*
* @author Madhura Bhave
*/
public class OAuth2RestOperationsConfigurationTests {
private ConfigurableApplicationContext context;
private ConfigurableEnvironment environment = new StandardEnvironment();
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void clientCredentialsWithClientId() throws Exception {
TestPropertyValues.of("security.oauth2.client.client-id=acme")
.applyTo(this.environment);
initializeContext(OAuth2RestOperationsConfiguration.class, true);
assertThat(this.context.getBean(OAuth2RestOperationsConfiguration.class))
.isNotNull();
assertThat(this.context.getBean(ClientCredentialsResourceDetails.class))
.isNotNull();
}
@Test
public void clientCredentialsWithNoClientId() throws Exception {
initializeContext(OAuth2RestOperationsConfiguration.class, true);
assertThat(this.context.getBean(OAuth2RestOperationsConfiguration.class))
.isNotNull();
assertThat(this.context.getBean(ClientCredentialsResourceDetails.class))
.isNotNull();
}
@Test
public void requestScopedWithClientId() throws Exception {
TestPropertyValues.of("security.oauth2.client.client-id=acme")
.applyTo(this.environment);
initializeContext(ConfigForRequestScopedConfiguration.class, false);
assertThat(this.context.containsBean("oauth2ClientContext")).isTrue();
}
@Test
public void requestScopedWithNoClientId() throws Exception {
initializeContext(ConfigForRequestScopedConfiguration.class, false);
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(DefaultOAuth2ClientContext.class);
}
@Test
public void sessionScopedWithClientId() throws Exception {
TestPropertyValues.of("security.oauth2.client.client-id=acme")
.applyTo(this.environment);
initializeContext(ConfigForSessionScopedConfiguration.class, false);
assertThat(this.context.containsBean("oauth2ClientContext")).isTrue();
}
@Test
public void sessionScopedWithNoClientId() throws Exception {
initializeContext(ConfigForSessionScopedConfiguration.class, false);
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(DefaultOAuth2ClientContext.class);
}
private void initializeContext(Class<?> configuration, boolean clientCredentials) {
this.context = new SpringApplicationBuilder(configuration)
.environment(this.environment).web(clientCredentials
? WebApplicationType.NONE : WebApplicationType.SERVLET)
.run();
}
@Configuration
@Import({ OAuth2RestOperationsConfiguration.class })
protected static class WebApplicationConfiguration {
@Bean
public MockServletWebServerFactory webServerFactory() {
return new MockServletWebServerFactory();
}
}
@Configuration
@Import({ SecurityProperties.class, OAuth2ClientConfiguration.class,
OAuth2RestOperationsConfiguration.class })
protected static class ConfigForSessionScopedConfiguration
extends WebApplicationConfiguration {
}
@Configuration
protected static class ConfigForRequestScopedConfiguration
extends WebApplicationConfiguration {
}
}

View File

@ -1,104 +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.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link FixedAuthoritiesExtractor}.
*
* @author Dave Syer
*/
public class FixedAuthoritiesExtractorTests {
private FixedAuthoritiesExtractor extractor = new FixedAuthoritiesExtractor();
private Map<String, Object> map = new LinkedHashMap<>();
@Test
public void authorities() {
this.map.put("authorities", "ROLE_ADMIN");
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[ROLE_ADMIN]");
}
@Test
public void authoritiesCommaSeparated() {
this.map.put("authorities", "ROLE_USER,ROLE_ADMIN");
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[ROLE_USER, ROLE_ADMIN]");
}
@Test
public void authoritiesArray() {
this.map.put("authorities", new String[] { "ROLE_USER", "ROLE_ADMIN" });
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[ROLE_USER, ROLE_ADMIN]");
}
@Test
public void authoritiesList() {
this.map.put("authorities", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[ROLE_USER, ROLE_ADMIN]");
}
@Test
public void authoritiesAsListOfMaps() {
this.map.put("authorities",
Arrays.asList(Collections.singletonMap("authority", "ROLE_ADMIN")));
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[ROLE_ADMIN]");
}
@Test
public void authoritiesAsListOfMapsWithStandardKey() {
Map<String, String> map = new LinkedHashMap<>();
map.put("role", "ROLE_ADMIN");
map.put("extra", "value");
this.map.put("authorities", Arrays.asList(map));
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[ROLE_ADMIN]");
}
@Test
public void authoritiesAsListOfMapsWithNonStandardKey() {
this.map.put("authorities",
Arrays.asList(Collections.singletonMap("any", "ROLE_ADMIN")));
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[ROLE_ADMIN]");
}
@Test
public void authoritiesAsListOfMapsWithMultipleNonStandardKeys() {
Map<String, String> map = new HashMap<>();
map.put("any", "ROLE_ADMIN");
map.put("foo", "bar");
this.map.put("authorities", Arrays.asList(map));
assertThat(this.extractor.extractAuthorities(this.map).toString())
.isEqualTo("[{foo=bar, any=ROLE_ADMIN}]");
}
}

View File

@ -1,105 +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.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OAuth2ResourceServerConfiguration} when there are multiple
* {@link ResourceServerConfiguration} beans.
*
* @author Dave Syer
*/
public class MultipleResourceServerConfigurationTests {
private AnnotationConfigWebApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void orderIsUnchangedWhenThereAreMultipleResourceServerConfigurations() {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(DoubleResourceConfiguration.class);
TestPropertyValues.of("security.oauth2.resource.tokenInfoUri:http://example.com",
"security.oauth2.client.clientId=acme").applyTo(this.context);
this.context.refresh();
assertThat(this.context
.getBean("adminResources", ResourceServerConfiguration.class).getOrder())
.isEqualTo(3);
assertThat(this.context
.getBean("otherResources", ResourceServerConfiguration.class).getOrder())
.isEqualTo(4);
}
@ImportAutoConfiguration({ OAuth2AutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
@EnableWebSecurity
@Configuration
protected static class DoubleResourceConfiguration {
@Bean
protected ResourceServerConfiguration adminResources() {
ResourceServerConfiguration resource = new ResourceServerConfiguration() {
// Switch off the Spring Boot @Autowired configurers
@Override
public void setConfigurers(List<ResourceServerConfigurer> configurers) {
super.setConfigurers(configurers);
}
};
resource.setOrder(3);
return resource;
}
@Bean
protected ResourceServerConfiguration otherResources() {
ResourceServerConfiguration resource = new ResourceServerConfiguration() {
// Switch off the Spring Boot @Autowired configurers
@Override
public void setConfigurers(List<ResourceServerConfigurer> configurers) {
super.setConfigurers(configurers);
}
};
resource.setOrder(4);
return resource;
}
}
}

View File

@ -1,224 +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 com.fasterxml.jackson.databind.ObjectMapper;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.context.support.StaticWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Tests for {@link ResourceServerProperties}.
*
* @author Dave Syer
* @author Vedran Pavic
* @author Madhura Bhave
*/
public class ResourceServerPropertiesTests {
private ResourceServerProperties properties = new ResourceServerProperties("client",
"secret");
private Errors errors = mock(Errors.class);
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
@SuppressWarnings("unchecked")
public void json() throws Exception {
this.properties.getJwt().setKeyUri("http://example.com/token_key");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(this.properties);
Map<String, Object> value = mapper.readValue(json, Map.class);
Map<String, Object> jwt = (Map<String, Object>) value.get("jwt");
assertThat(jwt.get("keyUri")).isNotNull();
}
@Test
public void validateWhenClientIdNullShouldNotFail() throws Exception {
this.properties = new ResourceServerProperties(null, "secret");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
@Test
public void validateWhenBothJwtAndJwkKeyUrisPresentShouldFail() throws Exception {
this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys");
this.properties.getJwt().setKeyUri("http://my-auth-server/token_key");
setListableBeanFactory();
this.thrown.expect(IllegalStateException.class);
this.thrown.expect(getMatcher("Only one of jwt.keyUri (or jwt.keyValue) "
+ "and jwk.keySetUri should be configured.", null));
this.properties.validate();
}
@Test
public void validateWhenBothJwtKeyValueAndJwkKeyUriPresentShouldFail()
throws Exception {
this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys");
this.properties.getJwt().setKeyValue("my-key");
setListableBeanFactory();
this.thrown.expect(IllegalStateException.class);
this.thrown.expect(getMatcher("Only one of jwt.keyUri (or jwt.keyValue) "
+ "and jwk.keySetUri should be configured.", null));
this.properties.validate();
}
@Test
public void validateWhenJwkKeySetUriProvidedShouldSucceed() throws Exception {
this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
@Test
public void validateWhenKeyValuePresentShouldSucceed() throws Exception {
this.properties.getJwt().setKeyValue("my-key");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
@Test
public void validateWhenKeysUriOrValuePresentAndUserInfoAbsentShouldNotFail()
throws Exception {
this.properties = new ResourceServerProperties("client", "");
this.properties.getJwk().setKeySetUri("http://my-auth-server/token_keys");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
@Test
public void validateWhenKeyConfigAbsentAndInfoUrisNotConfiguredShouldFail()
throws Exception {
setListableBeanFactory();
this.thrown.expect(IllegalStateException.class);
this.thrown.expect(getMatcher("Missing tokenInfoUri and userInfoUri and there"
+ " is no JWT verifier key", "tokenInfoUri"));
this.properties.validate();
}
@Test
public void validateWhenTokenUriConfiguredShouldNotFail() throws Exception {
this.properties.setTokenInfoUri("http://my-auth-server/userinfo");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
@Test
public void validateWhenUserInfoUriConfiguredShouldNotFail() throws Exception {
this.properties.setUserInfoUri("http://my-auth-server/userinfo");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
@Test
public void validateWhenTokenUriPreferredAndClientSecretAbsentShouldFail()
throws Exception {
this.properties = new ResourceServerProperties("client", "");
this.properties.setTokenInfoUri("http://my-auth-server/check_token");
this.properties.setUserInfoUri("http://my-auth-server/userinfo");
setListableBeanFactory();
this.thrown.expect(IllegalStateException.class);
this.thrown.expect(getMatcher("Missing client secret", "clientSecret"));
this.properties.validate();
}
@Test
public void validateWhenTokenUriAbsentAndClientSecretAbsentShouldNotFail()
throws Exception {
this.properties = new ResourceServerProperties("client", "");
this.properties.setUserInfoUri("http://my-auth-server/userinfo");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
@Test
public void validateWhenTokenUriNotPreferredAndClientSecretAbsentShouldNotFail()
throws Exception {
this.properties = new ResourceServerProperties("client", "");
this.properties.setPreferTokenInfo(false);
this.properties.setTokenInfoUri("http://my-auth-server/check_token");
this.properties.setUserInfoUri("http://my-auth-server/userinfo");
setListableBeanFactory();
this.properties.validate();
verifyZeroInteractions(this.errors);
}
private void setListableBeanFactory() {
ListableBeanFactory beanFactory = new StaticWebApplicationContext() {
@Override
public String[] getBeanNamesForType(Class<?> type,
boolean includeNonSingletons, boolean allowEagerInit) {
if (type.isAssignableFrom(
ResourceServerTokenServicesConfiguration.class)) {
return new String[] { "ResourceServerTokenServicesConfiguration" };
}
return new String[0];
}
};
this.properties.setBeanFactory(beanFactory);
}
private BaseMatcher<BindException> getMatcher(String message, String field) {
return new BaseMatcher<BindException>() {
@Override
public void describeTo(Description description) {
}
@Override
public boolean matches(Object item) {
BindException ex = (BindException) ((Exception) item).getCause();
ObjectError error = ex.getAllErrors().get(0);
boolean messageMatches = message.equals(error.getDefaultMessage());
if (field == null) {
return messageMatches;
}
String fieldErrors = ((FieldError) error).getField();
return messageMatches && fieldErrors.equals(field);
}
};
}
}

View File

@ -1,417 +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.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration;
import org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration;
import org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.MockServletWebServerFactory;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
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.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ResourceServerTokenServicesConfiguration}.
*
* @author Dave Syer
* @author Madhura Bhave
* @author Eddú Meléndez
*/
public class ResourceServerTokenServicesConfigurationTests {
private static String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n"
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnGp/Q5lh0P8nPL21oMMrt2RrkT9"
+ "AW5jgYwLfSUnJVc9G6uR3cXRRDCjHqWU5WYwivcF180A6CWp/ireQFFBNowgc5XaA0kPpzE"
+ "tgsA5YsNX7iSnUibB004iBTfU9hZ2Rbsc8cWqynT0RyN4TP1RYVSeVKvMQk4GT1r7JCEC+T"
+ "Nu1ELmbNwMQyzKjsfBXyIOCFU/E94ktvsTZUHF4Oq44DBylCDsS1k7/sfZC2G5EU7Oz0mhG"
+ "8+Uz6MSEQHtoIi6mc8u64Rwi3Z3tscuWG2ShtsUFuNSAFNkY7LkLn+/hxLCu2bNISMaESa8"
+ "dG22CIMuIeRLVcAmEWEWH5EEforTg+QIDAQAB\n-----END PUBLIC KEY-----";
private ConfigurableApplicationContext context;
private ConfigurableEnvironment environment = new StandardEnvironment();
@Rule
public ExpectedException thrown = ExpectedException.none();
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void useRemoteTokenServices() {
TestPropertyValues.of("security.oauth2.resource.tokenInfoUri:http://example.com")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
RemoteTokenServices services = this.context.getBean(RemoteTokenServices.class);
assertThat(services).isNotNull();
}
@Test
public void switchToUserInfo() {
TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
UserInfoTokenServices services = this.context
.getBean(UserInfoTokenServices.class);
assertThat(services).isNotNull();
}
@Test
public void userInfoWithAuthorities() {
TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(AuthoritiesConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
UserInfoTokenServices services = this.context
.getBean(UserInfoTokenServices.class);
assertThat(services).isNotNull();
assertThat(services).extracting("authoritiesExtractor")
.containsExactly(this.context.getBean(AuthoritiesExtractor.class));
}
@Test
public void userInfoWithPrincipal() {
TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(PrincipalConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
UserInfoTokenServices services = this.context
.getBean(UserInfoTokenServices.class);
assertThat(services).isNotNull();
assertThat(services).extracting("principalExtractor")
.containsExactly(this.context.getBean(PrincipalExtractor.class));
}
@Test
public void userInfoWithClient() {
TestPropertyValues.of("security.oauth2.client.client-id=acme",
"security.oauth2.resource.userInfoUri:http://example.com",
"server.port=-1", "debug=true").applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceNoClientConfiguration.class)
.environment(this.environment).web(WebApplicationType.SERVLET).run();
BeanDefinition bean = ((BeanDefinitionRegistry) this.context)
.getBeanDefinition("scopedTarget.oauth2ClientContext");
assertThat(bean.getScope()).isEqualTo("request");
}
@Test
public void preferUserInfo() {
TestPropertyValues
.of("security.oauth2.resource.userInfoUri:http://example.com",
"security.oauth2.resource.tokenInfoUri:http://example.com",
"security.oauth2.resource.preferTokenInfo:false")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
UserInfoTokenServices services = this.context
.getBean(UserInfoTokenServices.class);
assertThat(services).isNotNull();
}
@Test
public void userInfoWithCustomizer() {
TestPropertyValues
.of("security.oauth2.resource.userInfoUri:http://example.com",
"security.oauth2.resource.tokenInfoUri:http://example.com",
"security.oauth2.resource.preferTokenInfo:false")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class,
Customizer.class).environment(this.environment)
.web(WebApplicationType.NONE).run();
UserInfoTokenServices services = this.context
.getBean(UserInfoTokenServices.class);
assertThat(services).isNotNull();
}
@Test
public void switchToJwt() {
TestPropertyValues.of("security.oauth2.resource.jwt.keyValue=FOOBAR")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class);
assertThat(services).isNotNull();
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(RemoteTokenServices.class);
}
@Test
public void asymmetricJwt() {
TestPropertyValues.of("security.oauth2.resource.jwt.keyValue=" + PUBLIC_KEY)
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class);
assertThat(services).isNotNull();
}
@Test
public void jwkConfiguration() throws Exception {
TestPropertyValues
.of("security.oauth2.resource.jwk.key-set-uri=http://my-auth-server/token_keys")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
DefaultTokenServices services = this.context.getBean(DefaultTokenServices.class);
assertThat(services).isNotNull();
this.thrown.expect(NoSuchBeanDefinitionException.class);
this.context.getBean(RemoteTokenServices.class);
}
@Test
public void springSocialUserInfo() {
TestPropertyValues
.of("security.oauth2.resource.userInfoUri:http://example.com",
"spring.social.facebook.app-id=foo",
"spring.social.facebook.app-secret=bar")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(SocialResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.SERVLET).run();
ConnectionFactoryLocator connectionFactory = this.context
.getBean(ConnectionFactoryLocator.class);
assertThat(connectionFactory).isNotNull();
SpringSocialTokenServices services = this.context
.getBean(SpringSocialTokenServices.class);
assertThat(services).isNotNull();
}
@Test
public void customUserInfoRestTemplateFactory() {
TestPropertyValues.of("security.oauth2.resource.userInfoUri:http://example.com")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(
CustomUserInfoRestTemplateFactory.class, ResourceConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
assertThat(this.context.getBeansOfType(UserInfoRestTemplateFactory.class))
.hasSize(1);
assertThat(this.context.getBean(UserInfoRestTemplateFactory.class))
.isInstanceOf(CustomUserInfoRestTemplateFactory.class);
}
@Test
public void jwtAccessTokenConverterIsConfiguredWhenKeyUriIsProvided() {
TestPropertyValues
.of("security.oauth2.resource.jwt.key-uri=http://localhost:12345/banana")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(ResourceConfiguration.class,
JwtAccessTokenConverterRestTemplateCustomizerConfiguration.class)
.environment(this.environment).web(WebApplicationType.NONE).run();
assertThat(this.context.getBeansOfType(JwtAccessTokenConverter.class)).hasSize(1);
}
@Test
public void jwkTokenStoreShouldBeConditionalOnMissingBean() throws Exception {
TestPropertyValues
.of("security.oauth2.resource.jwk.key-set-uri=http://my-auth-server/token_keys")
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(JwkTokenStoreConfiguration.class,
ResourceConfiguration.class).environment(this.environment)
.web(WebApplicationType.NONE).run();
assertThat(this.context.getBeansOfType(JwkTokenStore.class)).hasSize(1);
}
@Test
public void jwtTokenStoreShouldBeConditionalOnMissingBean() throws Exception {
TestPropertyValues.of("security.oauth2.resource.jwt.keyValue=" + PUBLIC_KEY)
.applyTo(this.environment);
this.context = new SpringApplicationBuilder(JwtTokenStoreConfiguration.class,
ResourceConfiguration.class).environment(this.environment)
.web(WebApplicationType.NONE).run();
assertThat(this.context.getBeansOfType(JwtTokenStore.class)).hasSize(1);
}
@Configuration
@Import({ ResourceServerTokenServicesConfiguration.class,
ResourceServerPropertiesConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
@EnableConfigurationProperties(OAuth2ClientProperties.class)
protected static class ResourceConfiguration {
}
@Configuration
protected static class AuthoritiesConfiguration extends ResourceConfiguration {
@Bean
AuthoritiesExtractor authoritiesExtractor() {
return (map) -> AuthorityUtils
.commaSeparatedStringToAuthorityList("ROLE_ADMIN");
}
}
@Configuration
protected static class PrincipalConfiguration extends ResourceConfiguration {
@Bean
PrincipalExtractor principalExtractor() {
return (map) -> "boot";
}
}
@Import({ OAuth2RestOperationsConfiguration.class })
protected static class ResourceNoClientConfiguration extends ResourceConfiguration {
@Bean
public MockServletWebServerFactory webServerFactory() {
return new MockServletWebServerFactory();
}
}
@Configuration
protected static class ResourceServerPropertiesConfiguration {
private OAuth2ClientProperties credentials;
public ResourceServerPropertiesConfiguration(OAuth2ClientProperties credentials) {
this.credentials = credentials;
}
@Bean
public ResourceServerProperties resourceServerProperties() {
return new ResourceServerProperties(this.credentials.getClientId(),
this.credentials.getClientSecret());
}
}
@Import({ FacebookAutoConfiguration.class, SocialWebAutoConfiguration.class })
protected static class SocialResourceConfiguration extends ResourceConfiguration {
@Bean
public ServletWebServerFactory webServerFactory() {
return mock(ServletWebServerFactory.class);
}
}
@Component
protected static class Customizer implements UserInfoRestTemplateCustomizer {
@Override
public void customize(OAuth2RestTemplate template) {
template.getInterceptors()
.add((request, body, execution) -> execution.execute(request, body));
}
}
@Component
protected static class CustomUserInfoRestTemplateFactory
implements UserInfoRestTemplateFactory {
private final OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(
new AuthorizationCodeResourceDetails());
@Override
public OAuth2RestTemplate getUserInfoRestTemplate() {
return this.restTemplate;
}
}
@Configuration
static class JwtAccessTokenConverterRestTemplateCustomizerConfiguration {
@Bean
public JwtAccessTokenConverterRestTemplateCustomizer restTemplateCustomizer() {
return new MockRestCallCustomizer();
}
}
@Configuration
static class JwtTokenStoreConfiguration {
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter jwtTokenEnhancer) {
return new JwtTokenStore(jwtTokenEnhancer);
}
}
@Configuration
static class JwkTokenStoreConfiguration {
@Bean
public TokenStore tokenStore() {
return new JwkTokenStore("http://my.key-set.uri");
}
}
private static class MockRestCallCustomizer
implements JwtAccessTokenConverterRestTemplateCustomizer {
@Override
public void customize(RestTemplate template) {
template.getInterceptors().add((request, body, execution) -> {
String payload = "{\"value\":\"FOO\"}";
MockClientHttpResponse response = new MockClientHttpResponse(
payload.getBytes(), HttpStatus.OK);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response;
});
}
}
}

View File

@ -1,150 +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.Date;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
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.AuthorizationCodeResourceDetails;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link UserInfoTokenServices}.
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"security.oauth2.resource.userInfoUri:http://example.com",
"security.oauth2.client.clientId=foo" })
@DirtiesContext
public class UserInfoTokenServicesRefreshTokenTests {
@Rule
public ExpectedException expected = ExpectedException.none();
@LocalServerPort
private int port;
private UserInfoTokenServices services;
@Before
public void init() {
this.services = new UserInfoTokenServices(
"http://localhost:" + this.port + "/user", "foo");
}
@Test
public void sunnyDay() {
assertThat(this.services.loadAuthentication("FOO").getName()).isEqualTo("me");
}
@Test
public void withRestTemplate() {
OAuth2ProtectedResourceDetails resource = new AuthorizationCodeResourceDetails();
OAuth2ClientContext context = new DefaultOAuth2ClientContext();
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken("FOO");
token.setRefreshToken(new DefaultExpiringOAuth2RefreshToken("BAR", new Date(0L)));
context.setAccessToken(token);
this.services.setRestTemplate(new OAuth2RestTemplate(resource, context));
assertThat(this.services.loadAuthentication("FOO").getName()).isEqualTo("me");
assertThat(context.getAccessToken().getValue()).isEqualTo("FOO");
// The refresh token is still intact
assertThat(context.getAccessToken().getRefreshToken())
.isEqualTo(token.getRefreshToken());
}
@Test
public void withRestTemplateChangesState() {
OAuth2ProtectedResourceDetails resource = new AuthorizationCodeResourceDetails();
OAuth2ClientContext context = new DefaultOAuth2ClientContext();
context.setAccessToken(new DefaultOAuth2AccessToken("FOO"));
this.services.setRestTemplate(new OAuth2RestTemplate(resource, context));
assertThat(this.services.loadAuthentication("BAR").getName()).isEqualTo("me");
assertThat(context.getAccessToken().getValue()).isEqualTo("BAR");
}
@Configuration
@Import({ ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
@RestController
protected static class Application {
@RequestMapping("/user")
public User user(@RequestHeader("Authorization") String authorization) {
if (authorization.endsWith("EXPIRED")) {
throw new InvalidTokenException("Expired");
}
return new User();
}
@ExceptionHandler(InvalidTokenException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public void expired() {
}
}
public static class User {
private String userid = "me";
public String getUserid() {
return this.userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
}
}

View File

@ -1,100 +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.LinkedHashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link UserInfoTokenServices}.
*
* @author Dave Syer
*/
public class UserInfoTokenServicesTests {
@Rule
public ExpectedException expected = ExpectedException.none();
private UserInfoTokenServices services = new UserInfoTokenServices(
"http://example.com", "foo");
private BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
private OAuth2RestOperations template = mock(OAuth2RestOperations.class);
private Map<String, Object> map = new LinkedHashMap<>();
@Before
public void init() {
this.resource.setClientId("foo");
given(this.template.getForEntity(any(String.class), eq(Map.class)))
.willReturn(new ResponseEntity<>(this.map, HttpStatus.OK));
given(this.template.getAccessToken())
.willReturn(new DefaultOAuth2AccessToken("FOO"));
given(this.template.getResource()).willReturn(this.resource);
given(this.template.getOAuth2ClientContext())
.willReturn(mock(OAuth2ClientContext.class));
}
@Test
public void sunnyDay() {
this.services.setRestTemplate(this.template);
assertThat(this.services.loadAuthentication("FOO").getName())
.isEqualTo("unknown");
}
@Test
public void badToken() {
this.services.setRestTemplate(this.template);
given(this.template.getForEntity(any(String.class), eq(Map.class)))
.willThrow(new UserRedirectRequiredException("foo:bar",
Collections.<String, String>emptyMap()));
this.expected.expect(InvalidTokenException.class);
assertThat(this.services.loadAuthentication("FOO").getName())
.isEqualTo("unknown");
}
@Test
public void userId() {
this.map.put("userid", "spencer");
this.services.setRestTemplate(this.template);
assertThat(this.services.loadAuthentication("FOO").getName())
.isEqualTo("spencer");
}
}

View File

@ -1,93 +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.sso;
import javax.servlet.Filter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link OAuth2AutoConfiguration} with basic configuration.
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest
@TestPropertySource(properties = { "security.oauth2.client.clientId=client",
"security.oauth2.client.clientSecret=secret",
"security.oauth2.client.userAuthorizationUri=http://example.com/oauth/authorize",
"security.oauth2.client.accessTokenUri=http://example.com/oauth/token",
"security.oauth2.resource.jwt.keyValue=SSSSHHH" })
public class BasicOAuth2SsoConfigurationTests {
@Autowired
private WebApplicationContext context;
@Autowired
@Qualifier("springSecurityFilterChain")
private Filter filter;
private MockMvc mvc;
@Before
public void init() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
.addFilters(this.filter).build();
}
@Test
public void homePageIsSecure() throws Exception {
this.mvc.perform(get("/")).andExpect(status().isFound())
.andExpect(header().string("location", "http://localhost/login"));
}
@Test
public void homePageSends401ToXhr() throws Exception {
this.mvc.perform(get("/").header("X-Requested-With", "XMLHttpRequest"))
.andExpect(status().isUnauthorized());
}
@Configuration
@Import(OAuth2AutoConfiguration.class)
@EnableOAuth2Sso
@MinimalSecureWebConfiguration
protected static class TestConfiguration {
}
}

View File

@ -1,120 +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.sso;
import javax.servlet.Filter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link OAuth2AutoConfiguration} with custom configuration.
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest
@TestPropertySource(properties = { "security.oauth2.client.clientId=client",
"security.oauth2.client.clientSecret=secret",
"security.oauth2.client.authorizationUri=http://example.com/oauth/authorize",
"security.oauth2.client.tokenUri=http://example.com/oauth/token",
"security.oauth2.resource.jwt.keyValue=SSSSHHH" })
public class CustomOAuth2SsoConfigurationTests {
@Autowired
private WebApplicationContext context;
@Autowired
@Qualifier("springSecurityFilterChain")
private Filter filter;
private MockMvc mvc;
@Before
public void init() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
.addFilters(this.filter).build();
}
@Test
public void uiPageIsSecure() throws Exception {
this.mvc.perform(get("/ui/")).andExpect(status().isFound())
.andExpect(header().string("location", "http://localhost/login"));
}
@Test
public void uiPageSends401ToXhr() throws Exception {
this.mvc.perform(get("/ui/").header("X-Requested-With", "XMLHttpRequest"))
.andExpect(status().isUnauthorized());
}
@Test
public void uiTestPageIsAccessible() throws Exception {
this.mvc.perform(get("/ui/test")).andExpect(status().isOk())
.andExpect(content().string("test"));
}
@Configuration
@EnableOAuth2Sso
@Import(OAuth2AutoConfiguration.class)
@MinimalSecureWebConfiguration
protected static class TestConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/ui/**").authorizeRequests().antMatchers("/ui/test")
.permitAll().anyRequest().authenticated();
}
@RestController
public static class TestController {
@RequestMapping("/ui/test")
public String test() {
return "test";
}
}
}
}

View File

@ -1,116 +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.sso;
import javax.servlet.Filter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link OAuth2AutoConfiguration} with custom configuration.
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest
@TestPropertySource(properties = { "security.oauth2.client.clientId=client",
"security.oauth2.client.clientSecret=secret",
"security.oauth2.client.authorizationUri=http://example.com/oauth/authorize",
"security.oauth2.client.tokenUri=http://example.com/oauth/token",
"security.oauth2.resource.jwt.keyValue=SSSSHHH" })
public class CustomOAuth2SsoWithAuthenticationEntryPointConfigurationTests {
@Autowired
private WebApplicationContext context;
@Autowired
@Qualifier("springSecurityFilterChain")
private Filter filter;
private MockMvc mvc;
@Before
public void init() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context)
.addFilters(this.filter).build();
}
@Test
public void uiPageIsSecure() throws Exception {
this.mvc.perform(get("/ui/")).andExpect(status().isUnauthorized());
}
@Test
public void uiTestPageIsAccessible() throws Exception {
this.mvc.perform(get("/ui/test")).andExpect(status().isOk())
.andExpect(content().string("test"));
}
@Configuration
@EnableOAuth2Sso
@Import(OAuth2AutoConfiguration.class)
@MinimalSecureWebConfiguration
protected static class TestConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/ui/**").authorizeRequests().antMatchers("/ui/test")
.permitAll().anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint(
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
@RestController
public static class TestController {
@RequestMapping("/ui/test")
public String test() {
return "test";
}
}
}
}

View File

@ -1,83 +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.sso;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Test to validate that a custom {@link RestTemplate} can be defined with OAuth2 SSO.
*
* @author Stephane Nicoll
*/
@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest
@TestPropertySource(properties = { "security.oauth2.client.clientId=client",
"security.oauth2.client.clientSecret=secret",
"security.oauth2.client.userAuthorizationUri=http://example.com/oauth/authorize",
"security.oauth2.client.accessTokenUri=http://example.com/oauth/token",
"security.oauth2.resource.jwt.keyValue=SSSSHHH" })
public class CustomRestTemplateBasicOAuth2SsoConfigurationTests {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ObjectProvider<RestTemplate> restTemplate;
@Test
public void customRestTemplateCanBePrimary() {
RestTemplate restTemplate = this.restTemplate.getIfAvailable();
verifyZeroInteractions(restTemplate);
assertThat(this.applicationContext.getBeansOfType(RestTemplate.class)).hasSize(1);
}
@Configuration
@Import(OAuth2AutoConfiguration.class)
@EnableOAuth2Sso
@MinimalSecureWebConfiguration
protected static class TestConfiguration {
@Bean
@Primary
public RestTemplate myRestTemplate() {
return mock(RestTemplate.class);
}
}
}

View File

@ -1,45 +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.sso;
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.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, SecurityAutoConfiguration.class })
public @interface MinimalSecureWebConfiguration {
}

View File

@ -1,13 +0,0 @@
package org.test
@EnableAuthorizationServer
@EnableResourceServer
@RestController
class SampleController {
@RequestMapping("/")
def hello() {
[message: "Hello World!"]
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2012-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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Security OAuth2.
*
* @author Greg Turnquist
* @author Dave Syer
* @since 1.3.0
*/
public class SpringSecurityOAuth2CompilerAutoConfiguration
extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableAuthorizationServer",
"EnableResourceServer", "EnableOAuth2Client", "EnableOAuth2Sso");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies)
throws CompilationFailedException {
dependencies.add("spring-security-oauth2", "spring-boot-starter-web",
"spring-boot-starter-security");
}
@Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports.addImports(
"org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso");
imports.addStarImports(
"org.springframework.security.oauth2.config.annotation.web.configuration",
"org.springframework.security.access.prepost");
}
}

View File

@ -8,7 +8,6 @@ org.springframework.boot.cli.compiler.autoconfigure.JdbcCompilerAutoConfiguratio
org.springframework.boot.cli.compiler.autoconfigure.JmsCompilerAutoConfiguration
org.springframework.boot.cli.compiler.autoconfigure.TransactionManagementCompilerAutoConfiguration
org.springframework.boot.cli.compiler.autoconfigure.SpringIntegrationCompilerAutoConfiguration
org.springframework.boot.cli.compiler.autoconfigure.SpringSecurityOAuth2CompilerAutoConfiguration
org.springframework.boot.cli.compiler.autoconfigure.SpringSecurityCompilerAutoConfiguration
org.springframework.boot.cli.compiler.autoconfigure.SpringMobileCompilerAutoConfiguration
org.springframework.boot.cli.compiler.autoconfigure.SpringRetryCompilerAutoConfiguration

View File

@ -71,13 +71,6 @@ public class SampleIntegrationTests {
assertThat(output).contains("completed with the following parameters");
}
@Test
public void oauth2Sample() throws Exception {
String output = this.cli.run("oauth2.groovy");
assertThat(output).contains("security.oauth2.client.client-id");
assertThat(output).contains("security.oauth2.client.client-secret =");
}
@Test
public void jobWebSample() throws Exception {
String output = this.cli.run("job.groovy", "web.groovy", "foo=bar");

View File

@ -173,8 +173,6 @@
<spring-restdocs.version>1.2.1.RELEASE</spring-restdocs.version>
<spring-retry.version>1.2.1.RELEASE</spring-retry.version>
<spring-security.version>5.0.0.M4</spring-security.version>
<spring-security-jwt.version>1.0.8.RELEASE</spring-security-jwt.version>
<spring-security-oauth.version>2.2.0.RELEASE</spring-security-oauth.version>
<spring-session.version>2.0.0.M4</spring-session.version>
<spring-social.version>2.0.0.M4</spring-social.version>
<spring-social-facebook.version>3.0.0.M3</spring-social-facebook.version>
@ -2358,21 +2356,6 @@
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>${spring-security-jwt.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth</artifactId>
<version>${spring-security-oauth.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${spring-security-oauth.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>

View File

@ -772,11 +772,6 @@
<artifactId>spring-security-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-config</artifactId>

View File

@ -476,27 +476,8 @@ content into your application; rather pick only the properties that you need.
# SECURITY PROPERTIES
# ----------------------------------------
# SECURITY ({sc-spring-boot-autoconfigure}/security/SecurityProperties.{sc-ext}[SecurityProperties])
spring.security.filter.order=0 # Security filter chain order.
spring.security.filter.dispatcher-types=ASYNC,ERROR,REQUEST # Security filter chain dispatcher types.
# SECURITY OAUTH2 CLIENT ({sc-spring-boot-autoconfigure}/security/oauth2/OAuth2ClientProperties.{sc-ext}[OAuth2ClientProperties])
security.oauth2.client.client-id= # OAuth2 client id.
security.oauth2.client.client-secret= # OAuth2 client secret. A random secret is generated by default
# SECURITY OAUTH2 RESOURCES ({sc-spring-boot-autoconfigure}/security/oauth2/resource/ResourceServerProperties.{sc-ext}[ResourceServerProperties])
security.oauth2.resource.id= # Identifier of the resource.
security.oauth2.resource.jwt.key-uri= # The URI of the JWT token. Can be set if the value is not available and the key is public.
security.oauth2.resource.jwt.key-value= # The verification key of the JWT token. Can either be a symmetric secret or PEM-encoded RSA public key.
security.oauth2.resource.jwk.key-set-uri= # The URI for getting the set of keys that can be used to validate the token.
security.oauth2.resource.prefer-token-info=true # Use the token info, can be set to false to use the user info.
security.oauth2.resource.service-id=resource #
security.oauth2.resource.token-info-uri= # URI of the token decoding endpoint.
security.oauth2.resource.token-type= # The token type to send when using the userInfoUri.
security.oauth2.resource.user-info-uri= # URI of the user endpoint.
# SECURITY OAUTH2 SSO ({sc-spring-boot-autoconfigure}/security/oauth2/client/OAuth2SsoProperties.{sc-ext}[OAuth2SsoProperties])
security.oauth2.sso.login-path=/login # Path to the login page, i.e. the one that triggers the redirect to the OAuth2 Authorization Server
spring.security.filter.order=0 # Security filter chain order.
spring.security.filter.dispatcher-types=ASYNC,ERROR,REQUEST # Security filter chain dispatcher types.
# ----------------------------------------
# DATA PROPERTIES

View File

@ -2752,238 +2752,6 @@ explicitly configure the paths that you do want to override.
[[boot-features-security-oauth2]]
=== OAuth2
If you have `spring-security-oauth2` on your classpath you can take advantage of some
auto-configuration to make it easy to set up Authorization or Resource Server. For full
details, see the {spring-security-oauth2-reference}[Spring Security OAuth 2 Developers
Guide].
[[boot-features-security-oauth2-authorization-server]]
==== Authorization Server
To create an Authorization Server and grant access tokens you need to use
`@EnableAuthorizationServer` and provide `security.oauth2.client.client-id` and
`security.oauth2.client.client-secret]` properties. The client will be registered for you
in an in-memory repository.
Having done that you will be able to use the client credentials to create an access token,
for example:
[indent=0]
----
$ 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
`client-secret`. The user credentials are the normal Spring Security user details (which
default in Spring Boot to "`user`" and a random password).
To switch off the auto-configuration and configure the Authorization Server features
yourself just add a `@Bean` of type `AuthorizationServerConfigurer`.
[[boot-features-security-oauth2-resource-server]]
==== 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 application 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, one of the following options:
* `security.oauth2.resource.user-info-uri` to use the `/me` resource (e.g.
`\https://uaa.run.pivotal.io/userinfo` on Pivotal Web Services (PWS))
* `security.oauth2.resource.token-info-uri` to use the token decoding endpoint (e.g.
`\https://uaa.run.pivotal.io/check_token` on PWS).
If you specify both the `user-info-uri` and the `token-info-uri` then you can set a flag
to say that one is preferred over the other (`prefer-token-info=true` is the default).
Alternatively (instead of `user-info-uri` or `token-info-uri`) if the tokens are JWTs you
can configure a `security.oauth2.resource.jwt.key-value` 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
`security.oauth2.resource.jwt.key-uri`. E.g. on PWS:
[indent=0]
----
$ curl https://uaa.run.pivotal.io/token_key
{"alg":"SHA256withRSA","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"}
----
Additionally, if your authorization server has an endpoint that returns a set of JSON Web Keys(JWKs),
you can configure `security.oauth2.resource.jwk.key-set-uri`. E.g. on PWS:
[indent=0]
----
$ curl https://uaa.run.pivotal.io/token_keys
{"keys":[{"kid":"key-1","alg":"RS256","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"]}
----
NOTE: Configuring both JWT and JWK properties will cause an error. Only one of `security.oauth2.resource.jwt.key-uri`
(or `security.oauth2.resource.jwt.key-value`) and `security.oauth2.resource.jwk.key-set-uri` should be configured.
WARNING: If you use the `security.oauth2.resource.jwt.key-uri` or `security.oauth2.resource.jwk.key-set-uri`,
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.
OAuth2 resources are protected by a filter chain with order
`security.oauth2.resource.filter-order` and the default is after the filter protecting the
actuator endpoints by default (so actuator endpoints will stay on HTTP Basic unless you
change the order).
[[boot-features-security-oauth2-token-type]]
=== Token Type in User Info
Google, and certain other 3rd party identity providers, are 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
`security.oauth2.resource.token-type`.
[[boot-features-security-custom-user-info]]
=== Customizing the User Info RestTemplate
If you have a `user-info-uri`, the resource server features use an `OAuth2RestTemplate`
internally to fetch user details for authentication. This is provided as a `@Bean` of
type `UserInfoRestTemplateFactory`. 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. Alternatively, you could define your own `UserInfoRestTemplateFactory`
`@Bean` to take full control.
[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]
----
security:
oauth2:
resource:
jwt:
keyValue: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC...
-----END PUBLIC KEY-----
----
====
[[boot-features-security-custom-user-info-client]]
==== Client
To make your web-app into an OAuth2 client you can simply add `@EnableOAuth2Client` and
Spring Boot will create an `OAuth2ClientContext` and `OAuth2ProtectedResourceDetails` that
are necessary to create an `OAuth2RestOperations`. Spring Boot does not automatically
create such bean but you can easily create your own:
[source,java,indent=0]
----
@Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails details) {
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
----
NOTE: You may want to add a qualifier and review your configuration as more than one
`RestTemplate` may be defined in your application.
This configuration uses `security.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,indent=0]
----
security:
oauth2:
client:
clientId: bd1c0a783ccdd1c9b9e4
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
clientAuthenticationScheme: form
----
An application with this configuration will redirect to Github for authorization when 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
application 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
`security.oauth2.client.scope` (comma separated or an array in YAML). By default the scope
is empty and it is up 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 `security.oauth2.client.client-authentication-scheme`
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
`security.oauth2.client.*` properties are bound to an instance of
`AuthorizationCodeResourceDetails` so all its properties can be specified.
TIP: In a non-web application you can still create an `OAuth2RestOperations` and it
is still wired into the `security.oauth2.client.*` configuration. In this case it is a
"`client credentials token grant`" you will be asking for if you use it (and there is no
need to use `@EnableOAuth2Client` or `@EnableOAuth2Sso`). To prevent that infrastructure
to be defined, just remove the `security.oauth2.client.client-id` from your configuration
(or make it the empty string).
[[boot-features-security-oauth2-single-sign-on]]
==== Single Sign On
An OAuth2 Client can be used to fetch user details from the provider (if such features are
available) and then convert them into an `Authentication` token for Spring Security.
The Resource Server above support this via the `user-info-uri` property This is the basis
for a Single Sign On (SSO) protocol based on OAuth2, and Spring Boot makes it easy to
participate by providing an annotation `@EnableOAuth2Sso`. The Github client above can
protect all its resources and authenticate using the Github `/user/` endpoint, by adding
that annotation and declaring where to find the endpoint (in addition to the
`security.oauth2.client.*` configuration already listed above):
.application.yml
[source,yaml,indent=0]]
----
security:
oauth2:
...
resource:
userInfoUri: https://api.github.com/user
preferTokenInfo: false
----
Since all paths are secure by default, there is no "`home`" page that you can show to
unauthenticated users and invite them to login (by visiting the `/login` path, or the
path specified by `security.oauth2.sso.login-path`).
To customize the access rules or paths to protect, so you can add a "`home`" page for
instance, `@EnableOAuth2Sso` can be added to a `WebSecurityConfigurerAdapter` and the
annotation will cause it to be decorated and enhanced with the necessary pieces to get
the `/login` path working. For example, here we simply allow unauthenticated access
to the home page at "/" and keep the default for everything else:
[source,java,indent=0]
----
include::{code-examples}/web/security/UnauthenticatedAccessExample.java[tag=configuration]
----
[[boot-features-security-actuator]]
=== Actuator Security
If the Actuator is also in use, you will find:

View File

@ -146,15 +146,6 @@ The following sample applications are provided:
| link:spring-boot-sample-secure[spring-boot-sample-secure]
| Non-web application that uses Spring Security
| link:spring-boot-sample-secure-oauth2-actuator[spring-boot-sample-secure-oauth2-actuator]
| RESTful service secured using OAuth2 and Actuator
| link:spring-boot-sample-secure-oauth2[spring-boot-sample-secure-oauth2]
| RESTful service secured using OAuth2
| link:spring-boot-sample-secure-oauth2-resource[spring-boot-sample-secure-oauth2-resource]
| OAuth2 resource server
| link:spring-boot-sample-servlet[spring-boot-sample-servlet]
| Web application with a "raw" `Servlet` returning plain text content
@ -215,9 +206,6 @@ The following sample applications are provided:
| link:spring-boot-sample-web-secure-custom[spring-boot-sample-web-secure-custom]
| Web application with custom Spring Security configuration
| link:spring-boot-sample-web-secure-github[spring-boot-sample-web-secure-github]
| Web application with Spring Security configured to authenticate with GitHub using OAuth2
| link:spring-boot-sample-web-secure-jdbc[spring-boot-sample-web-secure-jdbc]
| Web application with Spring Security configured to use JDBC authentication

View File

@ -67,9 +67,6 @@
<module>spring-boot-sample-property-validation</module>
<module>spring-boot-sample-quartz</module>
<module>spring-boot-sample-secure</module>
<module>spring-boot-sample-secure-oauth2</module>
<module>spring-boot-sample-secure-oauth2-actuator</module>
<module>spring-boot-sample-secure-oauth2-resource</module>
<module>spring-boot-sample-servlet</module>
<module>spring-boot-sample-session</module>
<module>spring-boot-sample-simple</module>
@ -91,7 +88,6 @@
<module>spring-boot-sample-web-mustache</module>
<module>spring-boot-sample-web-secure</module>
<module>spring-boot-sample-web-secure-custom</module>
<module>spring-boot-sample-web-secure-github</module>
<module>spring-boot-sample-web-secure-jdbc</module>
<module>spring-boot-sample-web-static</module>
<module>spring-boot-sample-web-ui</module>

View File

@ -1,54 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-secure-oauth2-actuator</artifactId>
<name>spring-boot-sample-secure-oauth2-actuator</name>
<description>Spring Boot Security OAuth2 Actuator Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,28 +0,0 @@
package sample.secure.oauth2.actuator;
import org.springframework.boot.actuate.autoconfigure.security.EndpointRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Basic auth security for actuator endpoints.
*
* @author Madhura Bhave
*/
@Configuration
@Order(2) // before the resource server configuration
public class ActuatorSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests()
.antMatchers("/**").authenticated()
.and()
.httpBasic();
// @formatter:on
}
}

View File

@ -1,73 +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 sample.secure.oauth2.actuator;
import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableResourceServer
@RestController
public class SampleSecureOAuth2ActuatorApplication {
@GetMapping("/")
public Message home() {
return new Message("Hello World");
}
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User.withUsername("user").password("password").roles("USER").build());
return manager;
}
public static void main(String[] args) {
SpringApplication.run(SampleSecureOAuth2ActuatorApplication.class, args);
}
class Message {
private String id = UUID.randomUUID().toString();
private String value;
public Message(String value) {
this.value = value;
}
public String getId() {
return this.id;
}
public String getValue() {
return this.value;
}
}
}

View File

@ -1,5 +0,0 @@
server.port=8081
endpoints.default.web.enabled=true
security.oauth2.resource.id=service
security.oauth2.resource.userInfoUri=http://localhost:8080/user
logging.level.org.springframework.security=DEBUG

View File

@ -1,95 +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 sample.secure.oauth2.actuator;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.util.Base64Utils;
import org.springframework.web.context.WebApplicationContext;
import static org.hamcrest.CoreMatchers.containsString;
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
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleSecureOAuth2ActuatorApplicationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private FilterChainProxy filterChain;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build();
SecurityContextHolder.clearContext();
}
@Test
public void homePageSecuredByDefault() throws Exception {
this.mvc.perform(get("/")).andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate", containsString("Bearer")))
.andDo(print());
}
@Test
public void healthSecured() throws Exception {
this.mvc.perform(get("/application/health")).andExpect(status().isUnauthorized());
}
@Test
public void healthWithBasicAuthorization() throws Exception {
this.mvc.perform(get("/application/health").header("Authorization",
"Basic " + Base64Utils.encodeToString("user:password".getBytes())))
.andExpect(status().isOk());
}
@Test
public void envSecured() throws Exception {
this.mvc.perform(get("/application/env")).andExpect(status().isUnauthorized());
}
@Test
public void envWithBasicAuthorization() throws Exception {
this.mvc.perform(get("/application/env").header("Authorization",
"Basic " + Base64Utils.encodeToString("user:password".getBytes())))
.andExpect(status().isOk()).andDo(print());
}
}

View File

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-secure-oauth2-resource</artifactId>
<name>spring-boot-sample-secure-oauth2-resource</name>
<description>Spring Boot Security OAuth2 Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,110 +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 sample.secure.oauth2.resource;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Domain object for tracking flights
*
* @author Craig Walls
* @author Greg Turnquist
*/
@Entity
@JsonIgnoreProperties(ignoreUnknown = true)
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String origin;
private String destination;
private String airline;
private String flightNumber;
private Date date;
private String traveler;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrigin() {
return this.origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getDestination() {
return this.destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getAirline() {
return this.airline;
}
public void setAirline(String airline) {
this.airline = airline;
}
public String getFlightNumber() {
return this.flightNumber;
}
public void setFlightNumber(String flightNumber) {
this.flightNumber = flightNumber;
}
public Date getDate() {
return this.date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTraveler() {
return this.traveler;
}
public void setTraveler(String traveler) {
this.traveler = traveler;
}
}

View File

@ -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 sample.secure.oauth2.resource;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
/**
* Spring Data interface with secured methods
*
* @author Craig Walls
* @author Greg Turnquist
*/
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Override
Iterable<Flight> findAll();
@Override
Optional<Flight> findById(Long aLong);
@Override
<S extends Flight> S save(S entity);
}

View File

@ -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 sample.secure.oauth2.resource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@SpringBootApplication
@EnableResourceServer
public class SampleSecureOAuth2ResourceApplication
extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/flights/**").authorizeRequests().anyRequest().authenticated();
}
public static void main(String[] args) {
SpringApplication.run(SampleSecureOAuth2ResourceApplication.class, args);
}
}

View File

@ -1,5 +0,0 @@
server.port=8081
spring.datasource.platform=h2
security.oauth2.resource.id=service
security.oauth2.resource.userInfoUri=http://localhost:8080/user
logging.level.org.springframework.security=DEBUG

View File

@ -1,4 +0,0 @@
insert into FLIGHT
(id, origin, destination, airline, flight_number, traveler)
values
(1, 'Nashville', 'Dallas', 'Spring Ways', 'OAUTH2', 'Greg Turnquist');

View File

@ -1,83 +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 sample.secure.oauth2.resource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.hateoas.MediaTypes;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;
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.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
*
* @author Greg Turnquist
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleSecureOAuth2ResourceApplicationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private FilterChainProxy filterChain;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build();
SecurityContextHolder.clearContext();
}
@Test
public void homePageAvailable() throws Exception {
this.mvc.perform(get("/").accept(MediaTypes.HAL_JSON)).andExpect(status().isOk())
.andDo(print());
}
@Test
public void flightsSecuredByDefault() throws Exception {
this.mvc.perform(get("/flights").accept(MediaTypes.HAL_JSON))
.andExpect(status().isUnauthorized()).andDo(print());
this.mvc.perform(get("/flights/1").accept(MediaTypes.HAL_JSON))
.andExpect(status().isUnauthorized()).andDo(print());
}
@Test
public void profileAvailable() throws Exception {
this.mvc.perform(get("/profile").accept(MediaTypes.HAL_JSON))
.andExpect(status().isOk()).andDo(print());
}
}

View File

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-secure-oauth2</artifactId>
<name>Spring Boot Security OAuth2 Sample</name>
<description>Spring Boot Security OAuth2 Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,18 +0,0 @@
package sample.secure.oauth2;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
/**
* @author Madhura Bhave
*/
@Configuration
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("greg").password("turnquist")
.roles("read");
}
}

View File

@ -1,110 +0,0 @@
/*
* Copyright 2012-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 sample.secure.oauth2;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Domain object for tracking flights
*
* @author Craig Walls
* @author Greg Turnquist
*/
@Entity
@JsonIgnoreProperties(ignoreUnknown = true)
public class Flight {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String origin;
private String destination;
private String airline;
private String flightNumber;
private Date date;
private String traveler;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrigin() {
return this.origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getDestination() {
return this.destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getAirline() {
return this.airline;
}
public void setAirline(String airline) {
this.airline = airline;
}
public String getFlightNumber() {
return this.flightNumber;
}
public void setFlightNumber(String flightNumber) {
this.flightNumber = flightNumber;
}
public Date getDate() {
return this.date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTraveler() {
return this.traveler;
}
public void setTraveler(String traveler) {
this.traveler = traveler;
}
}

View File

@ -1,44 +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 sample.secure.oauth2;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.security.access.prepost.PreAuthorize;
/**
* Spring Data interface with secured methods
*
* @author Craig Walls
* @author Greg Turnquist
*/
public interface FlightRepository extends CrudRepository<Flight, Long> {
@Override
@PreAuthorize("#oauth2.hasScope('read')")
Iterable<Flight> findAll();
@Override
@PreAuthorize("#oauth2.hasScope('read')")
Optional<Flight> findById(Long aLong);
@Override
@PreAuthorize("#oauth2.hasScope('write')")
<S extends Flight> S save(S entity);
}

View File

@ -1,111 +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 sample.secure.oauth2;
import java.security.Principal;
import org.springframework.boot.SpringApplication;
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;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* After you launch the app, you can seek a bearer token like this:
*
* <pre>
* curl localhost:8080/oauth/token -d "grant_type=password&scope=read&username=greg&password=turnquist" -u foo:bar
* </pre>
*
* <ul>
* <li>grant_type=password (user credentials will be supplied)</li>
* <li>scope=read (read only scope)</li>
* <li>username=greg (username checked against user details service)</li>
* <li>password=turnquist (password checked against user details service)</li>
* <li>-u foo:bar (clientid:secret)</li>
* </ul>
*
* Response should be similar to this:
* <code>{"access_token":"533de99b-5a0f-4175-8afd-1a64feb952d5","token_type":"bearer","expires_in":43199,"scope":"read"}</code>
*
* With the token value, you can now interrogate the RESTful interface like this:
*
* <pre>
* curl -H "Authorization: bearer [access_token]" localhost:8080/flights/1
* </pre>
*
* You should then see the pre-loaded data like this:
*
* <pre>
* {
* "origin" : "Nashville",
* "destination" : "Dallas",
* "airline" : "Spring Ways",
* "flightNumber" : "OAUTH2",
* "date" : null,
* "traveler" : "Greg Turnquist",
* "_links" : {
* "self" : {
* "href" : "http://localhost:8080/flights/1"
* }
* }
* }
* </pre>
*
* Test creating a new entry:
*
* <pre>
* curl -i -H "Authorization: bearer [access token]" -H "Content-Type:application/json" localhost:8080/flights -X POST -d @flight.json
* </pre>
*
* Insufficient scope? (read not write) Ask for a new token!
*
* <pre>
* curl localhost:8080/oauth/token -d "grant_type=password&scope=write&username=greg&password=turnquist" -u foo:bar
*
* {"access_token":"cfa69736-e2aa-4ae7-abbb-3085acda560e","token_type":"bearer","expires_in":43200,"scope":"write"}
* </pre>
*
* Retry with the new token. There should be a Location header.
*
* <pre>
* Location: http://localhost:8080/flights/2
*
* curl -H "Authorization: bearer [access token]" localhost:8080/flights/2
* </pre>
*
* @author Craig Walls
* @author Greg Turnquist
*/
@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RestController
public class SampleSecureOAuth2Application {
@GetMapping("/user")
public Principal user(Principal user) {
return user;
}
public static void main(String[] args) {
SpringApplication.run(SampleSecureOAuth2Application.class, args);
}
}

View File

@ -1,7 +0,0 @@
spring.datasource.platform=h2
security.oauth2.client.client-id=foo
security.oauth2.client.client-secret=bar
security.oauth2.authorization.checkTokenAccess=isAuthenticated()
logging.level.org.springframework.security=DEBUG

View File

@ -1,4 +0,0 @@
insert into FLIGHT
(id, origin, destination, airline, flight_number, traveler)
values
(1, 'Nashville', 'Dallas', 'Spring Ways', 'OAUTH2', 'Greg Turnquist');

View File

@ -1,126 +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 sample.secure.oauth2;
import java.util.Base64;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.hateoas.MediaTypes;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.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;
/**
* Series of automated integration tests to verify proper behavior of auto-configured,
* OAuth2-secured system
*
* @author Greg Turnquist
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SampleSecureOAuth2ApplicationTests {
@Autowired
private WebApplicationContext context;
@Autowired
private FilterChainProxy filterChain;
private MockMvc mvc;
private final ObjectMapper objectMapper = new ObjectMapper();
@Before
public void setUp() {
this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build();
SecurityContextHolder.clearContext();
}
@Test
public void everythingIsSecuredByDefault() throws Exception {
this.mvc.perform(get("/").accept(MediaTypes.HAL_JSON))
.andExpect(status().isUnauthorized()).andDo(print());
this.mvc.perform(get("/flights").accept(MediaTypes.HAL_JSON))
.andExpect(status().isUnauthorized()).andDo(print());
this.mvc.perform(get("/flights/1").accept(MediaTypes.HAL_JSON))
.andExpect(status().isUnauthorized()).andDo(print());
this.mvc.perform(get("/alps").accept(MediaTypes.HAL_JSON))
.andExpect(status().isUnauthorized()).andDo(print());
}
@Test
@Ignore
public void accessingRootUriPossibleWithUserAccount() throws Exception {
String header = "Basic "
+ new String(Base64.getEncoder().encode("greg:turnquist".getBytes()));
this.mvc.perform(
get("/").accept(MediaTypes.HAL_JSON).header("Authorization", header))
.andExpect(
header().string("Content-Type", MediaTypes.HAL_JSON.toString()))
.andExpect(status().isOk()).andDo(print());
}
@Test
public void useAppSecretsPlusUserAccountToGetBearerToken() throws Exception {
String header = "Basic "
+ new String(Base64.getEncoder().encode("foo:bar".getBytes()));
MvcResult result = this.mvc
.perform(post("/oauth/token").header("Authorization", header)
.param("grant_type", "password").param("scope", "read")
.param("username", "greg").param("password", "turnquist"))
.andExpect(status().isOk()).andDo(print()).andReturn();
Object accessToken = this.objectMapper
.readValue(result.getResponse().getContentAsString(), Map.class)
.get("access_token");
MvcResult flightsAction = this.mvc
.perform(get("/flights/1").accept(MediaTypes.HAL_JSON)
.header("Authorization", "Bearer " + accessToken))
.andExpect(header().string("Content-Type",
MediaTypes.HAL_JSON.toString() + ";charset=UTF-8"))
.andExpect(status().isOk()).andDo(print()).andReturn();
Flight flight = this.objectMapper.readValue(
flightsAction.getResponse().getContentAsString(), Flight.class);
assertThat(flight.getOrigin()).isEqualTo("Nashville");
assertThat(flight.getDestination()).isEqualTo("Dallas");
assertThat(flight.getAirline()).isEqualTo("Spring Ways");
assertThat(flight.getFlightNumber()).isEqualTo("OAUTH2");
assertThat(flight.getTraveler()).isEqualTo("Greg Turnquist");
}
}

View File

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-web-secure-github</artifactId>
<name>Spring Boot Web Secure GitHub Sample</name>
<description>Spring Boot Web Secure GitHub Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,32 +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 sample.web.secure.github;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
@EnableOAuth2Sso
public class SampleGithubSecureApplication implements WebMvcConfigurer {
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleGithubSecureApplication.class, args);
}
}

View File

@ -1,11 +0,0 @@
security:
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

View File

@ -1,21 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<link rel="stylesheet" href="/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="http://spring.io"> Spring </a>
<ul class="nav">
<li><a href="/"> Home </a></li>
</ul>
</div>
</div>
<h1>Super Special Greeting</h1>
<div>Hello World</div>
</div>
</body>
</html>

View File

@ -1,70 +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 sample.web.secure.github;
import java.net.URI;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Basic integration tests for GitHub SSO application.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class SampleGithubApplicationTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void everythingIsSecuredByDefault() throws Exception {
ResponseEntity<Void> entity = this.restTemplate.getForEntity("/", Void.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation())
.isEqualTo(URI.create("http://localhost:" + this.port + "/login"));
}
@Test
public void loginRedirectsToGithub() throws Exception {
ResponseEntity<Void> entity = this.restTemplate.getForEntity("/login",
Void.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation().toString())
.startsWith("https://github.com/login/oauth");
}
}