Skip to content

Commit c5dc3f5

Browse files
author
Dave Syer
committed
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.
1 parent af320b4 commit c5dc3f5

File tree

29 files changed

+1026
-97
lines changed

29 files changed

+1026
-97
lines changed
Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@
1717
package org.springframework.boot.autoconfigure.security.oauth2;
1818

1919
import org.springframework.beans.BeansException;
20+
import org.springframework.beans.factory.annotation.Autowired;
2021
import org.springframework.beans.factory.config.BeanPostProcessor;
2122
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2223
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
2425
import org.springframework.boot.autoconfigure.security.oauth2.authserver.SpringSecurityOAuth2AuthorizationServerConfiguration;
25-
import org.springframework.boot.autoconfigure.security.oauth2.client.SpringSecurityOAuth2ClientConfiguration;
26-
import org.springframework.boot.autoconfigure.security.oauth2.resource.SpringSecurityOAuth2ResourceServerConfiguration;
26+
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration;
27+
import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration;
28+
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
29+
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration;
2730
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
2831
import org.springframework.boot.context.properties.EnableConfigurationProperties;
32+
import org.springframework.context.annotation.Bean;
2933
import org.springframework.context.annotation.Configuration;
3034
import org.springframework.context.annotation.Import;
3135
import org.springframework.security.oauth2.common.OAuth2AccessToken;
@@ -40,16 +44,25 @@
4044
*/
4145
@Configuration
4246
@ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurerAdapter.class })
43-
@ConditionalOnWebApplication
4447
@Import({ SpringSecurityOAuth2AuthorizationServerConfiguration.class,
45-
SpringSecurityOAuth2MethodSecurityConfiguration.class,
46-
SpringSecurityOAuth2ResourceServerConfiguration.class,
47-
SpringSecurityOAuth2ClientConfiguration.class })
48+
OAuth2MethodSecurityConfiguration.class,
49+
OAuth2ResourceServerConfiguration.class,
50+
OAuth2RestOperationsConfiguration.class })
4851
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
49-
@EnableConfigurationProperties(ClientCredentialsProperties.class)
50-
public class SpringSecurityOAuth2AutoConfiguration {
52+
@EnableConfigurationProperties(OAuth2ClientProperties.class)
53+
public class OAuth2AutoConfiguration {
54+
55+
@Autowired
56+
private OAuth2ClientProperties credentials;
57+
58+
@Bean
59+
public ResourceServerProperties resourceServerProperties() {
60+
return new ResourceServerProperties(this.credentials.getClientId(),
61+
this.credentials.getClientSecret());
62+
}
5163

5264
@Configuration
65+
@ConditionalOnWebApplication
5366
protected static class ResourceServerOrderProcessor implements BeanPostProcessor {
5467

5568
@Override
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* @author Dave Syer
2525
*/
2626
@ConfigurationProperties("spring.oauth2.client")
27-
public class ClientCredentialsProperties {
27+
public class OAuth2ClientProperties {
2828

2929
private String clientId;
3030

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/authserver/SpringSecurityOAuth2AuthorizationServerConfiguration.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@
2020
import java.util.Collections;
2121
import java.util.UUID;
2222

23+
import javax.annotation.PostConstruct;
24+
25+
import org.apache.commons.logging.Log;
26+
import org.apache.commons.logging.LogFactory;
2327
import org.springframework.beans.factory.annotation.Autowired;
2428
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2529
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2630
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
27-
import org.springframework.boot.autoconfigure.security.oauth2.ClientCredentialsProperties;
31+
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;
2832
import org.springframework.boot.context.properties.ConfigurationProperties;
2933
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3034
import org.springframework.context.annotation.Bean;
@@ -66,13 +70,34 @@ public class SpringSecurityOAuth2AuthorizationServerConfiguration extends
6670

6771
@Autowired(required = false)
6872
private TokenStore tokenStore;
73+
74+
@Configuration
75+
protected static class ClientDetailsLogger {
76+
77+
private static final Log logger = LogFactory
78+
.getLog(SpringSecurityOAuth2AuthorizationServerConfiguration.class);
79+
80+
@Autowired
81+
private OAuth2ClientProperties credentials;
82+
83+
@PostConstruct
84+
public void init() {
85+
String prefix = "spring.oauth2.client";
86+
boolean defaultSecret = this.credentials.isDefaultSecret();
87+
logger.info(String.format(
88+
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n",
89+
prefix, this.credentials.getClientId(), prefix,
90+
defaultSecret ? this.credentials.getClientSecret() : "****"));
91+
}
92+
93+
}
6994

7095
@Configuration
7196
@ConditionalOnMissingBean(BaseClientDetails.class)
7297
protected static class BaseClientDetailsConfiguration {
7398

7499
@Autowired
75-
private ClientCredentialsProperties client;
100+
private OAuth2ClientProperties client;
76101

77102
@Bean
78103
@ConfigurationProperties("spring.oauth2.client")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.security.oauth2.client;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration;
26+
import org.springframework.context.annotation.Import;
27+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
28+
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
29+
30+
/**
31+
* Configuration for OAuth2 Single Sign On (SSO). If there is an existing
32+
* {@link WebSecurityConfigurerAdapter} provided by the user and annotated with
33+
* <code>@EnableOAuth2Sso</code>, it is enhanced by adding an authentication filter and an
34+
* authentication entry point. If the user only has <code>@EnableOAuth2Sso</code> but not
35+
* on a WebSecurityConfigurerAdapter then one is added with all paths secured and with an
36+
* order that puts it ahead of the default HTTP Basic security chain in Spring Boot.
37+
*
38+
* @author Dave Syer
39+
*
40+
*/
41+
@Target(ElementType.TYPE)
42+
@Retention(RetentionPolicy.RUNTIME)
43+
@Documented
44+
@EnableOAuth2Client
45+
@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class, ResourceServerTokenServicesConfiguration.class })
46+
public @interface EnableOAuth2Sso {
47+
48+
}
Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,15 @@
1515
*/
1616
package org.springframework.boot.autoconfigure.security.oauth2.client;
1717

18-
import javax.annotation.PostConstruct;
1918
import javax.annotation.Resource;
2019

21-
import org.apache.commons.logging.Log;
22-
import org.apache.commons.logging.LogFactory;
23-
import org.springframework.beans.factory.annotation.Autowired;
2420
import org.springframework.beans.factory.annotation.Qualifier;
2521
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2622
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2723
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
2824
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2925
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
3026
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
31-
import org.springframework.boot.autoconfigure.security.oauth2.ClientCredentialsProperties;
3227
import org.springframework.boot.context.embedded.FilterRegistrationBean;
3328
import org.springframework.boot.context.properties.ConfigurationProperties;
3429
import org.springframework.context.annotation.Bean;
@@ -60,23 +55,7 @@
6055
@Configuration
6156
@ConditionalOnClass(EnableOAuth2Client.class)
6257
@ConditionalOnExpression("'${spring.oauth2.client.clientId:}'!=''")
63-
public class SpringSecurityOAuth2ClientConfiguration {
64-
65-
private static final Log logger = LogFactory
66-
.getLog(SpringSecurityOAuth2ClientConfiguration.class);
67-
68-
@Autowired
69-
private ClientCredentialsProperties credentials;
70-
71-
@PostConstruct
72-
public void init() {
73-
String prefix = "spring.oauth2.client";
74-
boolean defaultSecret = this.credentials.isDefaultSecret();
75-
logger.info(String.format(
76-
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n",
77-
prefix, this.credentials.getClientId(), prefix,
78-
defaultSecret ? this.credentials.getClientSecret() : "****"));
79-
}
58+
public class OAuth2RestOperationsConfiguration {
8059

8160
@Bean
8261
@Primary
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.security.oauth2.client;
18+
19+
import java.lang.reflect.Method;
20+
21+
import org.aopalliance.intercept.MethodInterceptor;
22+
import org.aopalliance.intercept.MethodInvocation;
23+
import org.springframework.aop.framework.ProxyFactory;
24+
import org.springframework.beans.BeansException;
25+
import org.springframework.beans.factory.BeanFactory;
26+
import org.springframework.beans.factory.BeanFactoryAware;
27+
import org.springframework.beans.factory.config.BeanPostProcessor;
28+
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
29+
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
30+
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoCustomConfiguration.WebSecurityEnhancerCondition;
31+
import org.springframework.context.annotation.ConditionContext;
32+
import org.springframework.context.annotation.Conditional;
33+
import org.springframework.context.annotation.Configuration;
34+
import org.springframework.context.annotation.ImportAware;
35+
import org.springframework.core.type.AnnotatedTypeMetadata;
36+
import org.springframework.core.type.AnnotationMetadata;
37+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
38+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
39+
import org.springframework.util.ClassUtils;
40+
import org.springframework.util.ReflectionUtils;
41+
42+
/**
43+
* Configuration for OAuth2 Single Sign On (SSO) when there is an existing
44+
* {@link WebSecurityConfigurerAdapter} provided by the user and annotated with
45+
* <code>@EnableOAuth2Sso</code>. The user-provided configuration is enhanced by adding an
46+
* authentication filter and an authentication entry point.
47+
*
48+
* @author Dave Syer
49+
*
50+
*/
51+
@Configuration
52+
@Conditional(WebSecurityEnhancerCondition.class)
53+
public class OAuth2SsoCustomConfiguration implements ImportAware, BeanPostProcessor,
54+
BeanFactoryAware {
55+
56+
private Class<?> configType;
57+
58+
private BeanFactory beanFactory;
59+
60+
@Override
61+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
62+
this.beanFactory = beanFactory;
63+
}
64+
65+
@Override
66+
public void setImportMetadata(AnnotationMetadata importMetadata) {
67+
configType = ClassUtils.resolveClassName(importMetadata.getClassName(), null);
68+
69+
}
70+
71+
@Override
72+
public Object postProcessBeforeInitialization(Object bean, String beanName)
73+
throws BeansException {
74+
return bean;
75+
}
76+
77+
@Override
78+
public Object postProcessAfterInitialization(Object bean, String beanName)
79+
throws BeansException {
80+
if (configType.isAssignableFrom(bean.getClass())
81+
&& bean instanceof WebSecurityConfigurerAdapter) {
82+
ProxyFactory factory = new ProxyFactory();
83+
factory.setTarget(bean);
84+
factory.addAdvice(new SsoSecurityAdapter(beanFactory));
85+
bean = factory.getProxy();
86+
}
87+
return bean;
88+
}
89+
90+
private static class SsoSecurityAdapter implements MethodInterceptor {
91+
92+
private SsoSecurityConfigurer configurer;
93+
94+
public SsoSecurityAdapter(BeanFactory beanFactory) {
95+
configurer = new SsoSecurityConfigurer(beanFactory);
96+
}
97+
98+
@Override
99+
public Object invoke(MethodInvocation invocation) throws Throwable {
100+
if (invocation.getMethod().getName().equals("init")) {
101+
Method method = ReflectionUtils.findMethod(
102+
WebSecurityConfigurerAdapter.class, "getHttp");
103+
ReflectionUtils.makeAccessible(method);
104+
HttpSecurity http = (HttpSecurity) ReflectionUtils.invokeMethod(method,
105+
(WebSecurityConfigurerAdapter) invocation.getThis());
106+
configurer.configure(http);
107+
}
108+
return invocation.proceed();
109+
}
110+
111+
}
112+
113+
protected static class WebSecurityEnhancerCondition extends SpringBootCondition {
114+
115+
@Override
116+
public ConditionOutcome getMatchOutcome(ConditionContext context,
117+
AnnotatedTypeMetadata metadata) {
118+
String[] enablers = context.getBeanFactory().getBeanNamesForAnnotation(
119+
EnableOAuth2Sso.class);
120+
for (String name : enablers) {
121+
if (context.getBeanFactory().isTypeMatch(name,
122+
WebSecurityConfigurerAdapter.class)) {
123+
return ConditionOutcome
124+
.match("found @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
125+
}
126+
}
127+
return ConditionOutcome
128+
.noMatch("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
129+
}
130+
}
131+
132+
}

0 commit comments

Comments
 (0)