Skip to content

Commit b62695d

Browse files
author
Dave Syer
committed
SECOAUTH-120: add factory bean for OAuth consumer so that externalization works
1 parent 3952c95 commit b62695d

File tree

6 files changed

+283
-110
lines changed

6 files changed

+283
-110
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright 2006-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.springframework.security.oauth.config;
14+
15+
import java.io.IOException;
16+
import java.security.cert.Certificate;
17+
import java.security.cert.CertificateException;
18+
import java.security.cert.CertificateFactory;
19+
20+
import org.springframework.beans.factory.BeanCreationException;
21+
import org.springframework.beans.factory.FactoryBean;
22+
import org.springframework.context.ResourceLoaderAware;
23+
import org.springframework.core.io.ResourceLoader;
24+
import org.springframework.security.core.authority.AuthorityUtils;
25+
import org.springframework.security.oauth.common.signature.RSAKeySecret;
26+
import org.springframework.security.oauth.common.signature.SharedConsumerSecret;
27+
import org.springframework.security.oauth.common.signature.SignatureSecret;
28+
import org.springframework.security.oauth.provider.BaseConsumerDetails;
29+
import org.springframework.security.oauth.provider.ConsumerDetails;
30+
31+
/**
32+
* @author Dave Syer
33+
*
34+
*/
35+
public class ConsumerDetailsFactoryBean implements FactoryBean<ConsumerDetails>, ResourceLoaderAware {
36+
37+
private Object typeOfSecret;
38+
private BaseConsumerDetails consumer = new BaseConsumerDetails();
39+
private String secret;
40+
private ResourceLoader resourceLoader;
41+
42+
public void setResourceLoader(ResourceLoader resourceLoader) {
43+
this.resourceLoader = resourceLoader;
44+
}
45+
46+
public void setSecret(String secret) {
47+
this.secret = secret;
48+
}
49+
50+
public void setConsumerKey(String consumerKey) {
51+
consumer.setConsumerKey(consumerKey);
52+
}
53+
54+
public void setConsumerName(String consumerName) {
55+
consumer.setConsumerName(consumerName);
56+
}
57+
58+
public void setSignatureSecret(SignatureSecret signatureSecret) {
59+
consumer.setSignatureSecret(signatureSecret);
60+
}
61+
62+
public void setAuthorities(String authorities) {
63+
consumer.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(authorities));
64+
}
65+
66+
public void setResourceName(String resourceName) {
67+
consumer.setResourceName(resourceName);
68+
}
69+
70+
public void setResourceDescription(String resourceDescription) {
71+
consumer.setResourceDescription(resourceDescription);
72+
}
73+
74+
public void setRequiredToObtainAuthenticatedToken(boolean requiredToObtainAuthenticatedToken) {
75+
consumer.setRequiredToObtainAuthenticatedToken(requiredToObtainAuthenticatedToken);
76+
}
77+
78+
public void setTypeOfSecret(Object typeOfSecret) {
79+
this.typeOfSecret = typeOfSecret;
80+
}
81+
82+
public ConsumerDetails getObject() throws Exception {
83+
if ("rsa-cert".equals(typeOfSecret)) {
84+
try {
85+
Certificate cert = CertificateFactory.getInstance("X.509").generateCertificate(resourceLoader.getResource(secret).getInputStream());
86+
consumer.setSignatureSecret(new RSAKeySecret(cert.getPublicKey()));
87+
}
88+
catch (IOException e) {
89+
throw new BeanCreationException("RSA certificate not found at " + secret + ".",
90+
e);
91+
}
92+
catch (CertificateException e) {
93+
throw new BeanCreationException("Invalid RSA certificate at " + secret + ".", e);
94+
}
95+
catch (NullPointerException e) {
96+
throw new BeanCreationException("Could not load RSA certificate at " + secret + ".", e);
97+
}
98+
}
99+
else {
100+
consumer.setSignatureSecret(new SharedConsumerSecret(secret));
101+
}
102+
return consumer;
103+
}
104+
105+
public Class<?> getObjectType() {
106+
return BaseConsumerDetails.class;
107+
}
108+
109+
public boolean isSingleton() {
110+
return true;
111+
}
112+
113+
}

spring-security-oauth/src/main/java/org/springframework/security/oauth/config/ConsumerServiceBeanDefinitionParser.java

Lines changed: 61 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -16,106 +16,88 @@
1616

1717
package org.springframework.security.oauth.config;
1818

19+
import java.util.List;
20+
21+
import org.springframework.beans.BeanMetadataElement;
1922
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
23+
import org.springframework.beans.factory.support.ManagedMap;
2024
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
2125
import org.springframework.beans.factory.xml.ParserContext;
22-
import org.springframework.security.core.authority.AuthorityUtils;
23-
import org.springframework.security.oauth.common.signature.RSAKeySecret;
24-
import org.springframework.security.oauth.common.signature.SharedConsumerSecret;
25-
import org.springframework.security.oauth.provider.BaseConsumerDetails;
2626
import org.springframework.security.oauth.provider.InMemoryConsumerDetailsService;
2727
import org.springframework.util.StringUtils;
2828
import org.springframework.util.xml.DomUtils;
2929
import org.w3c.dom.Element;
3030

31-
import java.io.IOException;
32-
import java.security.cert.Certificate;
33-
import java.security.cert.CertificateException;
34-
import java.security.cert.CertificateFactory;
35-
import java.util.List;
36-
import java.util.Map;
37-
import java.util.TreeMap;
38-
3931
/**
4032
* @author Ryan Heaton
4133
* @author Andrew McCall
34+
* @author Dave Syer
4235
*/
4336
public class ConsumerServiceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
4437

45-
@Override
46-
protected Class getBeanClass(Element element) {
47-
return InMemoryConsumerDetailsService.class;
48-
}
38+
@Override
39+
protected Class<?> getBeanClass(Element element) {
40+
return InMemoryConsumerDetailsService.class;
41+
}
42+
43+
@Override
44+
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
45+
List<Element> consumerElements = DomUtils.getChildElementsByTagName(element, "consumer");
46+
ManagedMap<String, BeanMetadataElement> consumers = new ManagedMap<String, BeanMetadataElement>();
47+
for (Object item : consumerElements) {
4948

50-
@Override
51-
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
52-
List consumerElements = DomUtils.getChildElementsByTagName(element, "consumer");
53-
Map<String, BaseConsumerDetails> consumers = new TreeMap<String, BaseConsumerDetails>();
54-
for (Object item : consumerElements) {
55-
BaseConsumerDetails consumer = new BaseConsumerDetails();
56-
Element consumerElement = (Element) item;
57-
String key = consumerElement.getAttribute("key");
58-
if (StringUtils.hasText(key)) {
59-
consumer.setConsumerKey(key);
60-
}
61-
else {
62-
parserContext.getReaderContext().error("A consumer key must be supplied with the definition of a consumer.", consumerElement);
63-
}
49+
BeanDefinitionBuilder consumer = BeanDefinitionBuilder
50+
.genericBeanDefinition(ConsumerDetailsFactoryBean.class);
51+
Element consumerElement = (Element) item;
52+
String key = consumerElement.getAttribute("key");
53+
if (StringUtils.hasText(key)) {
54+
consumer.addPropertyValue("consumerKey", key);
55+
}
56+
else {
57+
parserContext.getReaderContext().error(
58+
"A consumer key must be supplied with the definition of a consumer.", consumerElement);
59+
}
6460

65-
String secret = consumerElement.getAttribute("secret");
66-
if (secret != null) {
67-
String typeOfSecret = consumerElement.getAttribute("typeOfSecret");
68-
if ("rsa-cert".equals(typeOfSecret)) {
69-
try {
70-
Certificate cert = CertificateFactory.getInstance("X.509").generateCertificate(parserContext.getReaderContext().getResourceLoader().getResource(secret).getInputStream());
71-
consumer.setSignatureSecret(new RSAKeySecret(cert.getPublicKey()));
72-
}
73-
catch (IOException e) {
74-
parserContext.getReaderContext().error("RSA certificate not found at " + secret + ".", consumerElement, e);
75-
}
76-
catch (CertificateException e) {
77-
parserContext.getReaderContext().error("Invalid RSA certificate at " + secret + ".", consumerElement, e);
78-
}
79-
catch (NullPointerException e) {
80-
parserContext.getReaderContext().error("Could not load RSA certificate at " + secret + ".", consumerElement, e);
81-
}
82-
}
83-
else {
84-
consumer.setSignatureSecret(new SharedConsumerSecret(secret));
85-
}
86-
}
87-
else {
88-
parserContext.getReaderContext().error("A consumer secret must be supplied with the definition of a consumer.", consumerElement);
89-
}
61+
String secret = consumerElement.getAttribute("secret");
62+
if (StringUtils.hasText(secret)) {
63+
consumer.addPropertyValue("secret", secret);
64+
String typeOfSecret = consumerElement.getAttribute("typeOfSecret");
65+
consumer.addPropertyValue("typeOfSecret", typeOfSecret);
66+
}
67+
else {
68+
parserContext.getReaderContext().error(
69+
"A consumer secret must be supplied with the definition of a consumer.", consumerElement);
70+
}
9071

91-
String name = consumerElement.getAttribute("name");
92-
if (StringUtils.hasText(name)) {
93-
consumer.setConsumerName(name);
94-
}
72+
String name = consumerElement.getAttribute("name");
73+
if (StringUtils.hasText(name)) {
74+
consumer.addPropertyValue("consumerName", name);
75+
}
9576

96-
String authorities = consumerElement.getAttribute("authorities");
97-
if (authorities != null) {
98-
consumer.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(authorities));
99-
}
77+
String authorities = consumerElement.getAttribute("authorities");
78+
if (StringUtils.hasText(authorities)) {
79+
consumer.addPropertyValue("authorities", authorities);
80+
}
10081

101-
String resourceName = consumerElement.getAttribute("resourceName");
102-
if (resourceName != null) {
103-
consumer.setResourceName(resourceName);
104-
}
82+
String resourceName = consumerElement.getAttribute("resourceName");
83+
if (StringUtils.hasText(resourceName)) {
84+
consumer.addPropertyValue("resourceName", resourceName);
85+
}
10586

106-
String resourceDescription = consumerElement.getAttribute("resourceDescription");
107-
if (resourceDescription != null) {
108-
consumer.setResourceDescription(resourceDescription);
109-
}
87+
String resourceDescription = consumerElement.getAttribute("resourceDescription");
88+
if (StringUtils.hasText(resourceDescription)) {
89+
consumer.addPropertyValue("resourceDescription", resourceDescription);
90+
}
11091

111-
String requiredToObtainAuthenticatedToken = consumerElement.getAttribute("requiredToObtainAuthenticatedToken");
112-
if (requiredToObtainAuthenticatedToken != null && "false".equalsIgnoreCase(requiredToObtainAuthenticatedToken)) {
113-
consumer.setRequiredToObtainAuthenticatedToken(false);
114-
}
92+
String requiredToObtainAuthenticatedToken = consumerElement
93+
.getAttribute("requiredToObtainAuthenticatedToken");
94+
if (StringUtils.hasText(requiredToObtainAuthenticatedToken)) {
95+
consumer.addPropertyValue("requiredToObtainAuthenticatedToken", requiredToObtainAuthenticatedToken);
96+
}
11597

116-
consumers.put(key, consumer);
117-
}
98+
consumers.put(key, consumer.getBeanDefinition());
99+
}
118100

119-
builder.addPropertyValue("consumerDetailsStore", consumers);
120-
}
101+
builder.addPropertyValue("consumerDetailsStore", consumers);
102+
}
121103
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.springframework.security.oauth.config;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
6+
import org.junit.Test;
7+
import org.junit.runner.RunWith;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.security.oauth.common.signature.SharedConsumerSecret;
10+
import org.springframework.security.oauth.provider.ConsumerDetails;
11+
import org.springframework.security.oauth.provider.ConsumerDetailsService;
12+
import org.springframework.test.context.ContextConfiguration;
13+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14+
15+
@ContextConfiguration
16+
@RunWith(SpringJUnit4ClassRunner.class)
17+
public class TestConsumerServiceBeanDefinitionParser {
18+
19+
@Autowired
20+
private ConsumerDetailsService clientDetailsService;
21+
22+
@Test
23+
public void testClientDetailsFromNonPropertyFile() {
24+
assertNotNull(clientDetailsService.loadConsumerByConsumerKey("www.google.com"));
25+
}
26+
27+
@Test
28+
public void testClientDetailsFromPropertyFile() {
29+
ConsumerDetails consumer = clientDetailsService.loadConsumerByConsumerKey("my-client-key");
30+
assertNotNull(consumer);
31+
assertEquals("my-client-secret", ((SharedConsumerSecret)consumer.getSignatureSecret()).getConsumerSecret());
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans"
4+
xmlns:oauth="http://www.springframework.org/schema/security/oauth" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
6+
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
7+
http://www.springframework.org/schema/security/oauth http://www.springframework.org/schema/security/spring-security-oauth-1.0.xsd">
8+
9+
<http auto-config='true' access-denied-page="/login.jsp">
10+
<intercept-url pattern="/xml/photos" access="ROLE_USER" />
11+
<intercept-url pattern="/json/photos" access="ROLE_USER" />
12+
<intercept-url pattern="/photo/**" access="ROLE_USER" />
13+
<intercept-url pattern="/oauth/**" access="ROLE_USER" />
14+
<intercept-url pattern="/request_token_authorized.jsp" access="ROLE_USER" />
15+
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
16+
17+
<form-login authentication-failure-url="/login.jsp" default-target-url="/index.jsp" login-page="/login.jsp"
18+
login-processing-url="/login.do" />
19+
<logout logout-success-url="/index.jsp" logout-url="/logout.do" />
20+
</http>
21+
22+
<authentication-manager>
23+
<authentication-provider>
24+
<user-service>
25+
<user name="marissa" password="koala" authorities="ROLE_USER" />
26+
<user name="paul" password="emu" authorities="ROLE_USER" />
27+
</user-service>
28+
</authentication-provider>
29+
</authentication-manager>
30+
31+
<oauth:provider consumer-details-service-ref="consumerDetails" token-services-ref="tokenServices"
32+
request-token-url="/oauth/request_token" authenticate-token-url="/oauth/authorize" authentication-failed-url="/oauth/confirm_access"
33+
access-granted-url="/request_token_authorized.jsp" access-token-url="/oauth/access_token" require10a="false" />
34+
35+
<oauth:consumer-details-service id="consumerDetails">
36+
<oauth:consumer name="Tonr.com" key="tonr-consumer-key" secret="SHHHHH!!!!!!!!!!" resourceName="Your Photos"
37+
resourceDescription="Your photos that you have uploaded to sparklr.com." />
38+
<oauth:consumer name="iGoogle" key="www.google.com" secret="classpath:/org/springframework/security/oauth/config/igoogle.cert"
39+
typeOfSecret="rsa-cert" resourceName="Your Photos" resourceDescription="Your photos that you have uploaded to sparklr.com." />
40+
<oauth:consumer name="myClient" key="${my.client.key}" secret="${my.client.secret}"
41+
resourceName="Your Photos" resourceDescription="${my.client.resource.description}" />
42+
</oauth:consumer-details-service>
43+
44+
<oauth:token-services id="tokenServices" />
45+
46+
<beans:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
47+
<beans:property name="properties">
48+
<beans:value>
49+
my.client.key=my-client-key
50+
my.client.secret=my-client-secret
51+
my.client.resource.description=A Resource
52+
</beans:value>
53+
</beans:property>
54+
</beans:bean>
55+
56+
</beans:beans>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDBDCCAm2gAwIBAgIJAK8dGINfkSTHMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV
3+
BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzETMBEG
4+
A1UEChMKR29vZ2xlIEluYzEXMBUGA1UEAxMOd3d3Lmdvb2dsZS5jb20wHhcNMDgx
5+
MDA4MDEwODMyWhcNMDkxMDA4MDEwODMyWjBgMQswCQYDVQQGEwJVUzELMAkGA1UE
6+
CBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBJ
7+
bmMxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GN
8+
ADCBiQKBgQDQUV7ukIfIixbokHONGMW9+ed0E9X4m99I8upPQp3iAtqIvWs7XCbA
9+
bGqzQH1qX9Y00hrQ5RRQj8OI3tRiQs/KfzGWOdvLpIk5oXpdT58tg4FlYh5fbhIo
10+
VoVn4GvtSjKmJFsoM8NRtEJHL1aWd++dXzkQjEsNcBXwQvfDb0YnbQIDAQABo4HF
11+
MIHCMB0GA1UdDgQWBBSm/h1pNY91bNfW08ac9riYzs3cxzCBkgYDVR0jBIGKMIGH
12+
gBSm/h1pNY91bNfW08ac9riYzs3cx6FkpGIwYDELMAkGA1UEBhMCVVMxCzAJBgNV
13+
BAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUg
14+
SW5jMRcwFQYDVQQDEw53d3cuZ29vZ2xlLmNvbYIJAK8dGINfkSTHMAwGA1UdEwQF
15+
MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYpHTr3vQNsHHHUm4MkYcDB20a5KvcFoX
16+
gCcYtmdyd8rh/FKeZm2me7eQCXgBfJqQ4dvVLJ4LgIQiU3R5ZDe0WbW7rJ3M9ADQ
17+
FyQoRJP8OIMYW3BoMi0Z4E730KSLRh6kfLq4rK6vw7lkH9oynaHHWZSJLDAp17cP
18+
j+6znWkN9/g=
19+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)