Add @EnableOAuth2Sso and spring.oauth2.sso.*

User can enable OAuth2 SSO by declaring the intent (@EnableOAuth2Sso)
and also configuring the client properties (spring.oauth2.client.*).
The spring.oauth2.sso.* are only needed to change the path for the
login (defaults to /login) - any other security configuration for the
protected resources can be added in a WebSecurityConfigurerAdapter
which carries the @EnableOAuth2Sso annotation.
This commit is contained in:
Dave Syer 2015-05-23 17:37:14 +01:00
parent af320b49bf
commit c5dc3f564b
29 changed files with 1026 additions and 97 deletions

View File

@ -17,15 +17,19 @@
package org.springframework.boot.autoconfigure.security.oauth2;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.oauth2.authserver.SpringSecurityOAuth2AuthorizationServerConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.client.SpringSecurityOAuth2ClientConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.SpringSecurityOAuth2ResourceServerConfiguration;
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.ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration;
import org.springframework.boot.autoconfigure.web.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;
@ -40,16 +44,25 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
*/
@Configuration
@ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurerAdapter.class })
@ConditionalOnWebApplication
@Import({ SpringSecurityOAuth2AuthorizationServerConfiguration.class,
SpringSecurityOAuth2MethodSecurityConfiguration.class,
SpringSecurityOAuth2ResourceServerConfiguration.class,
SpringSecurityOAuth2ClientConfiguration.class })
OAuth2MethodSecurityConfiguration.class,
OAuth2ResourceServerConfiguration.class,
OAuth2RestOperationsConfiguration.class })
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties(ClientCredentialsProperties.class)
public class SpringSecurityOAuth2AutoConfiguration {
@EnableConfigurationProperties(OAuth2ClientProperties.class)
public class OAuth2AutoConfiguration {
@Autowired
private OAuth2ClientProperties credentials;
@Bean
public ResourceServerProperties resourceServerProperties() {
return new ResourceServerProperties(this.credentials.getClientId(),
this.credentials.getClientSecret());
}
@Configuration
@ConditionalOnWebApplication
protected static class ResourceServerOrderProcessor implements BeanPostProcessor {
@Override

View File

@ -24,7 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Dave Syer
*/
@ConfigurationProperties("spring.oauth2.client")
public class ClientCredentialsProperties {
public class OAuth2ClientProperties {
private String clientId;

View File

@ -20,11 +20,15 @@ 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.annotation.Autowired;
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.ClientCredentialsProperties;
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;
@ -66,13 +70,34 @@ public class SpringSecurityOAuth2AuthorizationServerConfiguration extends
@Autowired(required = false)
private TokenStore tokenStore;
@Configuration
protected static class ClientDetailsLogger {
private static final Log logger = LogFactory
.getLog(SpringSecurityOAuth2AuthorizationServerConfiguration.class);
@Autowired
private OAuth2ClientProperties credentials;
@PostConstruct
public void init() {
String prefix = "spring.oauth2.client";
boolean defaultSecret = this.credentials.isDefaultSecret();
logger.info(String.format(
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n",
prefix, this.credentials.getClientId(), prefix,
defaultSecret ? this.credentials.getClientSecret() : "****"));
}
}
@Configuration
@ConditionalOnMissingBean(BaseClientDetails.class)
protected static class BaseClientDetailsConfiguration {
@Autowired
private ClientCredentialsProperties client;
private OAuth2ClientProperties client;
@Bean
@ConfigurationProperties("spring.oauth2.client")

View File

@ -0,0 +1,48 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.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.context.annotation.Import;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
/**
* Configuration for OAuth2 Single Sign On (SSO). If there is an existing
* {@link WebSecurityConfigurerAdapter} provided by the user and annotated with
* <code>@EnableOAuth2Sso</code>, it is enhanced by adding an authentication filter and an
* authentication entry point. If the user only has <code>@EnableOAuth2Sso</code> but not
* on a WebSecurityConfigurerAdapter then one is added with all paths secured and with an
* order that puts it ahead of the default HTTP Basic security chain in Spring Boot.
*
* @author Dave Syer
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableOAuth2Client
@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class, ResourceServerTokenServicesConfiguration.class })
public @interface EnableOAuth2Sso {
}

View File

@ -15,12 +15,8 @@
*/
package org.springframework.boot.autoconfigure.security.oauth2.client;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -28,7 +24,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.oauth2.ClientCredentialsProperties;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
@ -60,23 +55,7 @@ import org.springframework.security.oauth2.provider.authentication.OAuth2Authent
@Configuration
@ConditionalOnClass(EnableOAuth2Client.class)
@ConditionalOnExpression("'${spring.oauth2.client.clientId:}'!=''")
public class SpringSecurityOAuth2ClientConfiguration {
private static final Log logger = LogFactory
.getLog(SpringSecurityOAuth2ClientConfiguration.class);
@Autowired
private ClientCredentialsProperties credentials;
@PostConstruct
public void init() {
String prefix = "spring.oauth2.client";
boolean defaultSecret = this.credentials.isDefaultSecret();
logger.info(String.format(
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n",
prefix, this.credentials.getClientId(), prefix,
defaultSecret ? this.credentials.getClientSecret() : "****"));
}
public class OAuth2RestOperationsConfiguration {
@Bean
@Primary

View File

@ -0,0 +1,132 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.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.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoCustomConfiguration.WebSecurityEnhancerCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.type.AnnotatedTypeMetadata;
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</code>. The user-provided configuration is enhanced by adding an
* authentication filter and an authentication entry point.
*
* @author Dave Syer
*
*/
@Configuration
@Conditional(WebSecurityEnhancerCondition.class)
public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProcessor,
BeanFactoryAware {
private Class<?> configType;
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
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 (configType.isAssignableFrom(bean.getClass())
&& bean instanceof WebSecurityConfigurerAdapter) {
ProxyFactory factory = new ProxyFactory();
factory.setTarget(bean);
factory.addAdvice(new SsoSecurityAdapter(beanFactory));
bean = factory.getProxy();
}
return bean;
}
private static class SsoSecurityAdapter implements MethodInterceptor {
private SsoSecurityConfigurer configurer;
public SsoSecurityAdapter(BeanFactory beanFactory) {
configurer = new SsoSecurityConfigurer(beanFactory);
}
@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,
(WebSecurityConfigurerAdapter) invocation.getThis());
configurer.configure(http);
}
return invocation.proceed();
}
}
protected static class WebSecurityEnhancerCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String[] enablers = context.getBeanFactory().getBeanNamesForAnnotation(
EnableOAuth2Sso.class);
for (String name : enablers) {
if (context.getBeanFactory().isTypeMatch(name,
WebSecurityConfigurerAdapter.class)) {
return ConditionOutcome
.match("found @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
}
}
return ConditionOutcome
.noMatch("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.client;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.util.ClassUtils;
/**
* If the user only has <code>@EnableOAuth2Sso</code> but not on a
* WebSecurityConfigurerAdapter then one is added with all paths secured and with an order
* that puts it ahead of the default HTTP Basic security chain in Spring Boot.
*
* @author Dave Syer
*
*/
@Configuration
@EnableConfigurationProperties(OAuth2SsoProperties.class)
public class OAuth2SsoDefaultConfiguration {
@Configuration
@Conditional(NeedsWebSecurityCondition.class)
protected static class WebSecurityConfiguration extends WebSecurityConfigurerAdapter
implements Ordered {
@Autowired
BeanFactory beanFactory;
@Autowired
OAuth2SsoProperties sso;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated();
new SsoSecurityConfigurer(beanFactory).configure(http);
}
@Override
public int getOrder() {
if (sso.getFilterOrder() != null) {
return sso.getFilterOrder();
}
if (ClassUtils
.isPresent(
"org.springframework.boot.actuate.autoconfigure.ManagementServerProperties",
null)) {
// If > BASIC_AUTH_ORDER then the existing rules for the actuator
// endpoints
// will take precedence. This value is < BASIC_AUTH_ORDER.
return SecurityProperties.ACCESS_OVERRIDE_ORDER - 5;
}
return SecurityProperties.ACCESS_OVERRIDE_ORDER;
}
}
private static class NeedsWebSecurityCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String[] enablers = context.getBeanFactory().getBeanNamesForAnnotation(
EnableOAuth2Sso.class);
for (String name : enablers) {
if (context.getBeanFactory().isTypeMatch(name,
WebSecurityConfigurerAdapter.class)) {
return ConditionOutcome
.noMatch("found @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
}
}
return ConditionOutcome
.match("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2013-2014 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;
/**
* @author Dave Syer
*
*/
@ConfigurationProperties("spring.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;
/**
* The filter order to apply if not providing an explicit WebSecurityConfigurerAdapter
* (in which case the order can be provided there instead).
*/
private Integer filterOrder;
public String getLoginPath() {
return loginPath;
}
public void setLoginPath(String loginPath) {
this.loginPath = loginPath;
}
public Integer getFilterOrder() {
return filterOrder;
}
public void setFilterOrder(Integer filterOrder) {
this.filterOrder = filterOrder;
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.client;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
class SsoSecurityConfigurer {
private BeanFactory beanFactory;
public SsoSecurityConfigurer(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void configure(HttpSecurity http) throws Exception {
OAuth2SsoProperties sso = beanFactory.getBean(OAuth2SsoProperties.class);
// Delay the processing of the filter until we know the
// SessionAuthenticationStrategy is available:
http.apply(new OAuth2ClientAuthenticationConfigurer(oauth2SsoFilter(sso)));
http.exceptionHandling().authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint(sso.getLoginPath()));
}
private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter(
OAuth2SsoProperties sso) {
OAuth2RestOperations restTemplate = beanFactory
.getBean(OAuth2RestOperations.class);
ResourceServerTokenServices tokenServices = beanFactory
.getBean(ResourceServerTokenServices.class);
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
sso.getLoginPath());
filter.setRestTemplate(restTemplate);
filter.setTokenServices(tokenServices);
return filter;
}
private static class OAuth2ClientAuthenticationConfigurer extends
SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private OAuth2ClientAuthenticationProcessingFilter filter;
public OAuth2ClientAuthenticationConfigurer(
OAuth2ClientAuthenticationProcessingFilter filter) {
this.filter = filter;
}
@Override
public void configure(HttpSecurity builder) throws Exception {
OAuth2ClientAuthenticationProcessingFilter ssoFilter = filter;
ssoFilter.setSessionAuthenticationStrategy(builder
.getSharedObject(SessionAuthenticationStrategy.class));
builder.addFilterAfter(ssoFilter,
AbstractPreAuthenticatedProcessingFilter.class);
}
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2;
package org.springframework.boot.autoconfigure.security.oauth2.method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@ -38,7 +38,7 @@ import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecur
@Configuration
@ConditionalOnClass({ OAuth2AccessToken.class })
@ConditionalOnBean(GlobalMethodSecurityConfiguration.class)
public class SpringSecurityOAuth2MethodSecurityConfiguration implements
public class OAuth2MethodSecurityConfiguration implements
BeanFactoryPostProcessor {
@Override

View File

@ -24,8 +24,7 @@ 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.ClientCredentialsProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.SpringSecurityOAuth2ResourceServerConfiguration.ResourceServerCondition;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration.ResourceServerCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
@ -58,7 +57,7 @@ import org.springframework.util.StringUtils;
@ConditionalOnWebApplication
@ConditionalOnBean(ResourceServerConfiguration.class)
@Import(ResourceServerTokenServicesConfiguration.class)
public class SpringSecurityOAuth2ResourceServerConfiguration {
public class OAuth2ResourceServerConfiguration {
@Autowired
private ResourceServerProperties resource;
@ -69,19 +68,6 @@ public class SpringSecurityOAuth2ResourceServerConfiguration {
return new ResourceSecurityConfigurer(this.resource);
}
@Configuration
protected static class ResourceServerPropertiesConfiguration {
@Autowired
private ClientCredentialsProperties credentials;
@Bean
public ResourceServerProperties resourceServerProperties() {
return new ResourceServerProperties(this.credentials.getClientId(),
this.credentials.getClientSecret());
}
}
protected static class ResourceSecurityConfigurer extends
ResourceServerConfigurerAdapter {

View File

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

View File

@ -29,8 +29,9 @@ import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.authserver.SpringSecurityOAuth2AuthorizationServerConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.SpringSecurityOAuth2ResourceServerConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
@ -112,8 +113,8 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
this.context.refresh();
this.context.getBean(SpringSecurityOAuth2AuthorizationServerConfiguration.class);
this.context.getBean(SpringSecurityOAuth2ResourceServerConfiguration.class);
this.context.getBean(SpringSecurityOAuth2MethodSecurityConfiguration.class);
this.context.getBean(OAuth2ResourceServerConfiguration.class);
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(BaseClientDetails.class);
AuthorizationEndpoint endpoint = this.context
@ -163,7 +164,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
assertThat(
this.context
.getBeanNamesForType(SpringSecurityOAuth2ResourceServerConfiguration.class).length,
.getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length,
is(0));
assertThat(
@ -183,7 +184,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
assertThat(
this.context
.getBeanNamesForType(SpringSecurityOAuth2ResourceServerConfiguration.class).length,
.getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length,
is(1));
assertThat(
@ -216,7 +217,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
assertThat(
this.context
.getBeanNamesForType(SpringSecurityOAuth2ResourceServerConfiguration.class).length,
.getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length,
is(1));
verifyAuthentication(config);
@ -247,7 +248,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
assertThat(
this.context
.getBeanNamesForType(SpringSecurityOAuth2ResourceServerConfiguration.class).length,
.getBeanNamesForType(OAuth2ResourceServerConfiguration.class).length,
is(1));
verifyAuthentication(config);
@ -260,7 +261,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
MinimalSecureWebApplication.class);
this.context.refresh();
this.context.getBean(SpringSecurityOAuth2MethodSecurityConfiguration.class);
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(ClientDetails.class);
@ -283,7 +284,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
MinimalSecureWebApplication.class);
this.context.refresh();
this.context.getBean(SpringSecurityOAuth2MethodSecurityConfiguration.class);
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(ClientDetails.class);
@ -306,7 +307,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
MinimalSecureWebApplication.class);
this.context.refresh();
this.context.getBean(SpringSecurityOAuth2MethodSecurityConfiguration.class);
this.context.getBean(OAuth2MethodSecurityConfiguration.class);
ClientDetails config = this.context.getBean(ClientDetails.class);
@ -403,7 +404,7 @@ public class SpringSecurityOAuth2AutoConfigurationTests {
@Import({ UseFreePortEmbeddedContainerConfiguration.class,
SecurityAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
SpringSecurityOAuth2AutoConfiguration.class, WebMvcAutoConfiguration.class,
OAuth2AutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class })
protected static class MinimalSecureWebApplication {

View File

@ -20,7 +20,7 @@ import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.ClientCredentialsProperties;
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration;
import org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
@ -139,7 +139,7 @@ public class ResourceServerTokenServicesConfigurationTests {
@Import({ ResourceServerTokenServicesConfiguration.class,
ResourceServerPropertiesConfiguration.class,
PropertyPlaceholderAutoConfiguration.class })
@EnableConfigurationProperties(ClientCredentialsProperties.class)
@EnableConfigurationProperties(OAuth2ClientProperties.class)
protected static class ResourceConfiguration {
}
@ -147,7 +147,7 @@ public class ResourceServerTokenServicesConfigurationTests {
protected static class ResourceServerPropertiesConfiguration {
@Autowired
private ClientCredentialsProperties credentials;
private OAuth2ClientProperties credentials;
@Bean
public ResourceServerProperties resourceServerProperties() {

View File

@ -0,0 +1,84 @@
/*
* Copyright 2013-2014 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 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;
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.autoconfigure.security.oauth2.sso.BasicOAuth2SsoConfigurationTests.TestConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* @author Dave Syer
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@WebAppConfiguration
@TestPropertySource(properties = { "spring.oauth2.client.clientId=client",
"spring.oauth2.client.clientSecret=secret",
"spring.oauth2.client.authorizationUri=http://example.com/oauth/authorize",
"spring.oauth2.client.tokenUri=http://example.com/oauth/token",
"spring.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() {
mvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filter).build();
}
@Test
public void homePageIsSecure() throws Exception {
mvc.perform(get("/")).andExpect(status().isFound())
.andExpect(header().string("location", "http://localhost/login"));
}
@Configuration
@Import(OAuth2AutoConfiguration.class)
@EnableOAuth2Sso
@MinimalSecureWebConfiguration
protected static class TestConfiguration {
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright 2013-2014 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 static org.hamcrest.Matchers.startsWith;
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;
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.autoconfigure.security.oauth2.sso.CustomOAuth2SsoConfigurationTests.TestConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
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.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
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;
/**
* @author Dave Syer
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@WebAppConfiguration
@TestPropertySource(properties = { "spring.oauth2.client.clientId=client",
"spring.oauth2.client.clientSecret=secret",
"spring.oauth2.client.authorizationUri=http://example.com/oauth/authorize",
"spring.oauth2.client.tokenUri=http://example.com/oauth/token",
"spring.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() {
mvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filter).build();
}
@Test
public void homePageIsBasicAuth() throws Exception {
mvc.perform(get("/")).andExpect(status().isUnauthorized())
.andExpect(header().string("WWW-Authenticate", startsWith("Basic")));
}
@Test
public void uiPageIsSecure() throws Exception {
mvc.perform(get("/ui/")).andExpect(status().isFound())
.andExpect(header().string("location", "http://localhost/login"));
}
@Test
public void uiTestPageIsAccessible() throws Exception {
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(value = "/ui/test")
public String test() {
return "test";
}
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.oauth2.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.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class,
SecurityAutoConfiguration.class }) @interface MinimalSecureWebConfiguration {
}

View File

@ -2,11 +2,9 @@ package org.test
@EnableAuthorizationServer
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RestController
class SampleController {
@PreAuthorize("#oauth2.hasScope('read')")
@RequestMapping("/")
def hello() {
[message: "Hello World!"]

View File

@ -31,20 +31,21 @@ public class SpringSecurityCompilerAutoConfiguration extends CompilerAutoConfigu
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableWebSecurity");
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableWebSecurity", "EnableGlobalMethodSecurity");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies
.ifAnyMissingClasses(
"org.springframework.security.config.annotation.web.configuration.EnableWebSecurity")
.add("spring-security-config").add("spring-security-web", false);
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity")
.add("spring-boot-starter-security");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports("org.springframework.security.core.Authentication",
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity",
"org.springframework.security.core.authority.AuthorityUtils")
.addStarImports(
"org.springframework.security.config.annotation.web.configuration",

View File

@ -1,9 +1,5 @@
/*
<<<<<<< HEAD
* Copyright 2012-2014 the original author or authors.
=======
* Copyright 2012-2013 the original author or authors.
>>>>>>> 12b17e3... Add Spring Security OAuth2 support to Spring Boot CLI
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,13 +27,14 @@ import org.springframework.boot.cli.compiler.DependencyCustomizer;
* {@link CompilerAutoConfiguration} for Spring Security OAuth2.
*
* @author Greg Turnquist
* @author Dave Syer
*/
public class SpringSecurityOAuth2CompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode,
"EnableAuthorizationServer", "EnableResourceServer");
"EnableAuthorizationServer", "EnableResourceServer", "EnableOAuth2Client", "EnableOAuth2Sso");
}
@Override
@ -50,7 +47,7 @@ public class SpringSecurityOAuth2CompilerAutoConfiguration extends CompilerAutoC
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports
.addImports(
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity")
"org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso")
.addStarImports(
"org.springframework.security.oauth2.config.annotation.web.configuration",
"org.springframework.security.access.prepost");

View File

@ -16,6 +16,11 @@
package org.springframework.boot.cli;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.net.URI;
@ -23,11 +28,6 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Integration tests to exercise the samples.
*
@ -70,15 +70,10 @@ public class SampleIntegrationTests {
}
@Test
@Ignore("Spring Security Oauth2 autoconfiguration reports bean creation issue with methodSecurityInterceptor")
public void oauth2Sample() throws Exception {
String output = this.cli.run("oauth2.groovy");
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.clientId"));
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.secret = ****"));
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.resourceId"));
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.authorizationTypes"));
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.scopes"));
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.redirectUris"));
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.client.clientId"));
assertTrue("Wrong output: " + output, output.contains("spring.oauth2.client.secret ="));
}
@Test

View File

@ -1471,11 +1471,9 @@ configuring some property values in the `Environment`.
To create an Authorization Server and grant access tokens you need to
`@EnableAuthorizationServer` and provide
`spring.oauth2.client.[clientId,clientSecret]`. The client will be
registered for you in an in-memory repository. To switch off the
autoconfiguration and configure the Authorization Server features
yourself just add a `@Bean` of type
`AuthorizationServerConfigurer`. Having done that you will be able to
usethe client credentials to create an access token, e.g.
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,
e.g.
----
$ curl client:secret@localhost:8080/oauth/token -d grant_type=password -d username=user -d password=pwd
@ -1486,6 +1484,10 @@ and secret, and 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 autoconfiguration and configure the Authorization
Server features yourself just add a `@Bean` of type
`AuthorizationServerConfigurer`.
==== Resource Server
To use the access token you need a Resource Server (which can be the
@ -1588,9 +1590,6 @@ spring:
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
clientAuthenticationScheme: form
resource:
userInfoUri: https://api.github.com/user
preferTokenInfo: false
----
An app with this configuration will redirect to github for
@ -1614,6 +1613,69 @@ fact, the `spring.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 `@Autowired` an
`OAuth2RestOperations` and it is still wired into the
`spring.oauth2.client.*` configuration, but 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 switch it off, just remove the
`spring.oauth2.client.clientId` from your configuration (or make it
the empty string).
==== Single Sign On
An OAuth2 Client can be used to fetch user details from the provider
if such features are provided (e.g. by using the `userInfoUri` that
the Resource Server supports as above), and then the user details can
be converted to an `Authentication` token for Spring Security. 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 `spring.oauth2.client.*` configuration already listed
above):
.application.yml
[source,yaml]
----
spring:
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
`spring.oauth2.sso.loginPath`).
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] ----
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void init(WebSecurity web) {
web.ignore("/");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated();
}
}
----
=== Actuator Security
If the Actuator is also in use, you will find:

View File

@ -77,6 +77,7 @@
<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-jsp</module>

View File

@ -0,0 +1,53 @@
<?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>1.3.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-web-secure-github</artifactId>
<name>spring-boot-sample-web-secure-github</name>
<description>Spring Boot Web Secure 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>
<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>
<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

@ -0,0 +1,32 @@
/*
* Copyright 2012-2014 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.ui.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.WebMvcConfigurerAdapter;
@SpringBootApplication
@EnableOAuth2Sso
public class SampleGithubSecureApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleGithubSecureApplication.class, args);
}
}

View File

@ -0,0 +1,11 @@
spring:
oauth2:
client:
clientId: bd1c0a783ccdd1c9b9e4
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
clientAuthenticationScheme: form
resource:
userInfoUri: https://api.github.com/user
preferTokenInfo: false

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
<!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

@ -0,0 +1,79 @@
/*
* Copyright 2012-2014 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.ui.github;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
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.SpringApplicationConfiguration;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;
/**
* Basic integration tests for github sso application.
*
* @author Dave Syer
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleGithubSecureApplication.class)
@WebAppConfiguration
@DirtiesContext
public class SampleGithubApplicationTests {
@Autowired
WebApplicationContext context;
@Autowired
FilterChainProxy filterChain;
@Autowired
OAuth2ClientContextFilter filter;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = webAppContextSetup(this.context).addFilters(this.filter, this.filterChain)
.build();
SecurityContextHolder.clearContext();
}
@Test
public void everythingIsSecuredByDefault() throws Exception {
this.mvc.perform(get("/")).andExpect(status().isFound())
.andExpect(redirectedUrlPattern("**/login"));
}
@Test
public void loginRedirectsToGithub() throws Exception {
this.mvc.perform(get("/login")).andExpect(status().isFound())
.andExpect(redirectedUrlPattern("https://github.com/**"));
}
}