Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
af320b49
Commit
af320b49
authored
May 22, 2015
by
Dave Syer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rationalize some features and merge in customizers from Spring Cloud
parent
5468949a
Changes
14
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
502 additions
and
161 deletions
+502
-161
SpringSecurityOAuth2ClientConfiguration.java
...auth2/client/SpringSecurityOAuth2ClientConfiguration.java
+97
-64
JwtAccessTokenConverterConfigurer.java
...ty/oauth2/resource/JwtAccessTokenConverterConfigurer.java
+24
-0
ResourceServerProperties.java
...re/security/oauth2/resource/ResourceServerProperties.java
+14
-0
ResourceServerTokenServicesConfiguration.java
...h2/resource/ResourceServerTokenServicesConfiguration.java
+125
-33
UserInfoRestTemplateCustomizer.java
...urity/oauth2/resource/UserInfoRestTemplateCustomizer.java
+41
-0
UserInfoTokenServices.java
...igure/security/oauth2/resource/UserInfoTokenServices.java
+15
-28
spring.factories
...utoconfigure/src/main/resources/META-INF/spring.factories
+1
-1
AutoConfigureConfigurationClassTests.java
...t/autoconfigure/AutoConfigureConfigurationClassTests.java
+1
-1
SpringSecurityOAuth2AutoConfigurationTests.java
...ty/oauth2/SpringSecurityOAuth2AutoConfigurationTests.java
+4
-6
UserInfoTokenServicesTests.java
.../security/oauth2/resource/UserInfoTokenServicesTests.java
+2
-3
pom.xml
spring-boot-dependencies/pom.xml
+1
-1
spring-boot-features.adoc
spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
+156
-0
Application.java
...ample-secure-oauth2/src/main/java/sample/Application.java
+2
-6
ApplicationTests.java
...-secure-oauth2/src/test/java/sample/ApplicationTests.java
+19
-18
No files found.
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/SpringSecurityOAuth2ClientConfiguration.java
View file @
af320b49
...
...
@@ -15,9 +15,6 @@
*/
package
org
.
springframework
.
boot
.
autoconfigure
.
security
.
oauth2
.
client
;
import
java.io.IOException
;
import
java.util.Arrays
;
import
javax.annotation.PostConstruct
;
import
javax.annotation.Resource
;
...
...
@@ -27,6 +24,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.beans.factory.annotation.Qualifier
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnBean
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnClass
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnExpression
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
;
import
org.springframework.boot.autoconfigure.security.oauth2.ClientCredentialsProperties
;
import
org.springframework.boot.context.embedded.FilterRegistrationBean
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
...
...
@@ -35,25 +36,22 @@ import org.springframework.context.annotation.Configuration;
import
org.springframework.context.annotation.Primary
;
import
org.springframework.context.annotation.Scope
;
import
org.springframework.context.annotation.ScopedProxyMode
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.HttpRequest
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.client.ClientHttpRequestExecution
;
import
org.springframework.http.client.ClientHttpRequestInterceptor
;
import
org.springframework.http.client.ClientHttpResponse
;
import
org.springframework.security.core.Authentication
;
import
org.springframework.security.core.context.SecurityContextHolder
;
import
org.springframework.security.oauth2.client.DefaultOAuth2ClientContext
;
import
org.springframework.security.oauth2.client.OAuth2ClientContext
;
import
org.springframework.security.oauth2.client.OAuth2RestOperations
;
import
org.springframework.security.oauth2.client.OAuth2RestTemplate
;
import
org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter
;
import
org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails
;
import
org.springframework.security.oauth2.client.token.AccessTokenRequest
;
import
org.springframework.security.oauth2.client.token.
RequestEnhancer
;
import
org.springframework.security.oauth2.client.token.grant.c
ode.AuthorizationCodeAccessTokenProvider
;
import
org.springframework.security.oauth2.client.token.
DefaultAccessTokenRequest
;
import
org.springframework.security.oauth2.client.token.grant.c
lient.ClientCredentialsResourceDetails
;
import
org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails
;
import
org.springframework.security.oauth2.common.DefaultOAuth2AccessToken
;
import
org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client
;
import
org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration
;
import
org.springframework.util.MultiValueMap
;
import
org.springframework.security.oauth2.provider.OAuth2Authentication
;
import
org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails
;
/**
* @author Dave Syer
...
...
@@ -61,84 +59,119 @@ import org.springframework.util.MultiValueMap;
*/
@Configuration
@ConditionalOnClass
(
EnableOAuth2Client
.
class
)
@ConditionalOn
Bean
(
OAuth2ClientConfiguration
.
class
)
@ConditionalOn
Expression
(
"'${spring.oauth2.client.clientId:}'!=''"
)
public
class
SpringSecurityOAuth2ClientConfiguration
{
private
static
final
Log
logger
=
LogFactory
.
getLog
(
SpringSecurityOAuth2ClientConfiguration
.
class
);
@Autowired
private
ClientCredentialsProperties
credentials
;
@PostConstruct
public
void
init
()
{
String
prefix
=
"spring.oauth2.client"
;
boolean
defaultSecret
=
this
.
credentials
.
isDefaultSecret
();
logger
.
info
(
String
.
format
(
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n"
,
prefix
,
this
.
credentials
.
getClientId
(),
prefix
,
defaultSecret
?
this
.
credentials
.
getClientSecret
()
:
"****"
));
}
@Bean
@Primary
public
OAuth2RestTemplate
oauth2RestTemplate
(
OAuth2ClientContext
oauth2ClientContext
,
OAuth2ProtectedResourceDetails
details
)
{
OAuth2RestTemplate
template
=
new
OAuth2RestTemplate
(
details
,
oauth2ClientContext
);
return
template
;
}
@Configuration
p
ublic
static
class
ClientAuthenticationFilter
Configuration
{
p
rotected
abstract
static
class
Base
Configuration
{
@Resource
@Qualifier
(
"accessTokenRequest"
)
private
AccessTokenRequest
accessTokenRequest
;
@Autowired
private
ClientCredentialsProperties
credentials
;
@PostConstruct
public
void
init
()
{
String
prefix
=
"spring.oauth2.client"
;
boolean
defaultSecret
=
this
.
credentials
.
isDefaultSecret
();
logger
.
info
(
String
.
format
(
"Initialized OAuth2 Client\n\n%s.clientId = %s\n%s.secret = %s\n\n"
,
prefix
,
this
.
credentials
.
getClientId
(),
prefix
,
defaultSecret
?
this
.
credentials
.
getClientSecret
()
:
"****"
));
@Bean
@ConfigurationProperties
(
"spring.oauth2.client"
)
@Primary
public
AuthorizationCodeResourceDetails
oauth2RemoteResource
()
{
AuthorizationCodeResourceDetails
details
=
new
AuthorizationCodeResourceDetails
();
return
details
;
}
}
@Configuration
@ConditionalOnNotWebApplication
protected
static
class
SingletonScopedConfiguration
{
@Bean
@ConfigurationProperties
(
"spring.oauth2.client"
)
@Primary
public
AuthorizationCodeResourceDetails
authorizationCodeResourceDetails
()
{
AuthorizationCodeResourceDetails
details
=
new
AuthorizationCodeResourceDetails
();
details
.
setClientSecret
(
this
.
credentials
.
getClientSecret
());
details
.
setClientId
(
this
.
credentials
.
getClientId
());
public
ClientCredentialsResourceDetails
oauth2RemoteResource
()
{
ClientCredentialsResourceDetails
details
=
new
ClientCredentialsResourceDetails
();
return
details
;
}
@Bean
public
OAuth2ClientContext
oauth2ClientContext
()
{
return
new
DefaultOAuth2ClientContext
(
new
DefaultAccessTokenRequest
());
}
}
@Configuration
@ConditionalOnBean
(
OAuth2ClientConfiguration
.
class
)
@ConditionalOnWebApplication
protected
static
class
SessionScopedConfiguration
extends
BaseConfiguration
{
@Resource
@Qualifier
(
"accessTokenRequest"
)
protected
AccessTokenRequest
accessTokenRequest
;
@Bean
@Scope
(
value
=
"session"
,
proxyMode
=
ScopedProxyMode
.
INTERFACES
)
public
OAuth2ClientContext
oauth2ClientContext
()
{
return
new
DefaultOAuth2ClientContext
(
accessTokenRequest
);
}
@Bean
public
FilterRegistrationBean
oauth2ClientFilterRegistration
(
OAuth2ClientContextFilter
filter
)
{
FilterRegistrationBean
registration
=
new
FilterRegistrationBean
();
registration
.
setFilter
(
filter
);
registration
.
setOrder
(
0
);
registration
.
setOrder
(
-
10
0
);
return
registration
;
}
@Bean
public
OAuth2RestOperations
authorizationCodeRestTemplate
(
AuthorizationCodeResourceDetails
oauth2RemoteResource
)
{
OAuth2RestTemplate
template
=
new
OAuth2RestTemplate
(
oauth2RemoteResource
,
oauth2ClientContext
());
template
.
setInterceptors
(
Arrays
.<
ClientHttpRequestInterceptor
>
asList
(
new
ClientHttpRequestInterceptor
()
{
@Override
public
ClientHttpResponse
intercept
(
HttpRequest
request
,
byte
[]
body
,
ClientHttpRequestExecution
execution
)
throws
IOException
{
request
.
getHeaders
().
setAccept
(
Arrays
.
asList
(
MediaType
.
APPLICATION_JSON
));
return
execution
.
execute
(
request
,
body
);
}
}));
AuthorizationCodeAccessTokenProvider
accessTokenProvider
=
new
AuthorizationCodeAccessTokenProvider
();
accessTokenProvider
.
setTokenRequestEnhancer
(
new
RequestEnhancer
()
{
@Override
public
void
enhance
(
AccessTokenRequest
request
,
OAuth2ProtectedResourceDetails
resource
,
MultiValueMap
<
String
,
String
>
form
,
HttpHeaders
headers
)
{
headers
.
setAccept
(
Arrays
.
asList
(
MediaType
.
APPLICATION_JSON
));
}
});
template
.
setAccessTokenProvider
(
accessTokenProvider
);
return
template
;
}
}
/*
* When the authentication is per cookie but the stored token is an oauth2 one, we can
* pass that on to a client that wants to call downstream. We don't even need an
* OAuth2ClientContextFilter until we need to refresh the access token. To handle
* refresh tokens you need to <code>@EnableOAuth2Client</code>
*/
@Configuration
@ConditionalOnMissingBean
(
OAuth2ClientConfiguration
.
class
)
@ConditionalOnWebApplication
protected
static
class
RequestScopedConfiguration
extends
BaseConfiguration
{
@Bean
@Scope
(
value
=
"session"
,
proxyMode
=
ScopedProxyMode
.
INTERFACES
)
public
OAuth2ClientContext
oauth2ClientContext
()
{
return
new
DefaultOAuth2ClientContext
(
this
.
accessTokenRequest
);
DefaultOAuth2ClientContext
context
=
new
DefaultOAuth2ClientContext
(
new
DefaultAccessTokenRequest
());
Authentication
principal
=
SecurityContextHolder
.
getContext
()
.
getAuthentication
();
if
(
principal
instanceof
OAuth2Authentication
)
{
OAuth2Authentication
authentication
=
(
OAuth2Authentication
)
principal
;
Object
details
=
authentication
.
getDetails
();
if
(
details
instanceof
OAuth2AuthenticationDetails
)
{
OAuth2AuthenticationDetails
oauthsDetails
=
(
OAuth2AuthenticationDetails
)
details
;
String
token
=
oauthsDetails
.
getTokenValue
();
context
.
setAccessToken
(
new
DefaultOAuth2AccessToken
(
token
));
}
}
return
context
;
}
}
...
...
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/JwtAccessTokenConverterConfigurer.java
0 → 100644
View file @
af320b49
/*
* Copyright 2013-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
autoconfigure
.
security
.
oauth2
.
resource
;
import
org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter
;
public
interface
JwtAccessTokenConverterConfigurer
{
void
configure
(
JwtAccessTokenConverter
converter
);
}
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java
View file @
af320b49
...
...
@@ -21,6 +21,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
import
org.springframework.beans.factory.BeanFactoryUtils
;
import
org.springframework.beans.factory.ListableBeanFactory
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.security.oauth2.common.DefaultOAuth2AccessToken
;
import
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration
;
import
org.springframework.util.StringUtils
;
import
org.springframework.validation.Errors
;
...
...
@@ -66,6 +67,11 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
*/
private
boolean
preferTokenInfo
=
true
;
/**
* The token type to send when using the userInfoUri.
*/
private
String
tokenType
=
DefaultOAuth2AccessToken
.
BEARER_TYPE
;
private
Jwt
jwt
=
new
Jwt
();
public
ResourceServerProperties
()
{
...
...
@@ -126,6 +132,14 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
this
.
preferTokenInfo
=
preferTokenInfo
;
}
public
String
getTokenType
()
{
return
tokenType
;
}
public
void
setTokenType
(
String
tokenType
)
{
this
.
tokenType
=
tokenType
;
}
public
Jwt
getJwt
()
{
return
this
.
jwt
;
}
...
...
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java
View file @
af320b49
This diff is collapsed.
Click to expand it.
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateCustomizer.java
0 → 100644
View file @
af320b49
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org
.
springframework
.
boot
.
autoconfigure
.
security
.
oauth2
.
resource
;
import
org.springframework.security.oauth2.client.OAuth2RestTemplate
;
/**
* Callback for customizing the rest template used to fetch user details if authentication
* is done via OAuth2 access tokens. The default should be fine for most providers, but
* occasionally you might need to add additional interceptors, or change the request
* authenticator (which is how the token gets attached to outgoing requests). The rest
* template that is being customized here is <i>only</i> used internally to carry out
* authentication (in the SSO or Resource Server use cases).
*
* @author Dave Syer
*
*/
public
interface
UserInfoRestTemplateCustomizer
{
/**
* Customize the rest template before it is initialized.
*
* @param template the rest template
*/
void
customize
(
OAuth2RestTemplate
template
);
}
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServices.java
View file @
af320b49
...
...
@@ -15,11 +15,7 @@
*/
package
org
.
springframework
.
boot
.
autoconfigure
.
security
.
oauth2
.
resource
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
...
...
@@ -44,22 +40,21 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
private
String
clientId
;
private
Collection
<
OAuth2RestOperations
>
resources
=
Collections
.
emptySet
();
private
OAuth2RestOperations
restTemplate
;
private
String
tokenType
=
DefaultOAuth2AccessToken
.
BEARER_TYPE
;
public
UserInfoTokenServices
(
String
userInfoEndpointUrl
,
String
clientId
)
{
this
.
userInfoEndpointUrl
=
userInfoEndpointUrl
;
this
.
clientId
=
clientId
;
}
public
void
setTokenType
(
String
tokenType
)
{
this
.
tokenType
=
tokenType
;
}
public
void
setResources
(
Map
<
String
,
OAuth2RestOperations
>
resources
)
{
this
.
resources
=
new
ArrayList
<
OAuth2RestOperations
>();
for
(
Entry
<
String
,
OAuth2RestOperations
>
key
:
resources
.
entrySet
())
{
OAuth2RestOperations
value
=
key
.
getValue
();
String
clientIdForTemplate
=
value
.
getResource
().
getClientId
();
if
(
clientIdForTemplate
!=
null
&&
clientIdForTemplate
.
equals
(
clientId
))
{
this
.
resources
.
add
(
value
);
}
}
public
void
setRestTemplate
(
OAuth2RestOperations
restTemplate
)
{
this
.
restTemplate
=
restTemplate
;
}
@Override
...
...
@@ -87,8 +82,8 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
}
private
Object
getPrincipal
(
Map
<
String
,
Object
>
map
)
{
String
[]
keys
=
new
String
[]
{
"user"
,
"username"
,
"userid"
,
"user_id"
,
"login"
,
"id"
};
String
[]
keys
=
new
String
[]
{
"user"
,
"username"
,
"userid"
,
"user_id"
,
"login"
,
"id"
,
"name"
};
for
(
String
key
:
keys
)
{
if
(
map
.
containsKey
(
key
))
{
return
map
.
get
(
key
);
...
...
@@ -104,23 +99,15 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
private
Map
<
String
,
Object
>
getMap
(
String
path
,
String
accessToken
)
{
logger
.
info
(
"Getting user info from: "
+
path
);
OAuth2RestOperations
restTemplate
=
null
;
for
(
OAuth2RestOperations
candidate
:
resources
)
{
try
{
if
(
accessToken
.
equals
(
candidate
.
getAccessToken
().
getValue
()))
{
restTemplate
=
candidate
;
}
}
catch
(
Exception
e
)
{
}
}
OAuth2RestOperations
restTemplate
=
this
.
restTemplate
;
if
(
restTemplate
==
null
)
{
BaseOAuth2ProtectedResourceDetails
resource
=
new
BaseOAuth2ProtectedResourceDetails
();
resource
.
setClientId
(
clientId
);
restTemplate
=
new
OAuth2RestTemplate
(
resource
);
restTemplate
.
getOAuth2ClientContext
().
setAccessToken
(
new
DefaultOAuth2AccessToken
(
accessToken
));
}
DefaultOAuth2AccessToken
token
=
new
DefaultOAuth2AccessToken
(
accessToken
);
token
.
setTokenType
(
tokenType
);
restTemplate
.
getOAuth2ClientContext
().
setAccessToken
(
token
);
@SuppressWarnings
(
"rawtypes"
)
Map
map
=
restTemplate
.
getForEntity
(
path
,
Map
.
class
).
getBody
();
@SuppressWarnings
(
"unchecked"
)
...
...
spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
View file @
af320b49
...
...
@@ -51,8 +51,8 @@ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\
org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.SpringSecurityOAuth2AutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\
org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\
org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\
...
...
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigureConfigurationClassTests.java
View file @
af320b49
...
...
@@ -19,7 +19,7 @@ package org.springframework.boot.autoconfigure;
import
org.springframework.boot.test.AbstractConfigurationClassTests
;
/**
* Tests for the autoconfigure module's
@Configuration
classes
* Tests for the autoconfigure module's
<code>@Configuration</code>
classes
* @author Andy Wilkinson
*/
public
class
AutoConfigureConfigurationClassTests
extends
AbstractConfigurationClassTests
{
...
...
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/SpringSecurityOAuth2AutoConfigurationTests.java
View file @
af320b49
...
...
@@ -16,12 +16,15 @@
package
org
.
springframework
.
boot
.
autoconfigure
.
security
.
oauth2
;
import
static
org
.
hamcrest
.
CoreMatchers
.
equalTo
;
import
static
org
.
hamcrest
.
CoreMatchers
.
is
;
import
static
org
.
hamcrest
.
MatcherAssert
.
assertThat
;
import
java.net.URI
;
import
java.util.Arrays
;
import
java.util.List
;
import
org.junit.Test
;
import
org.springframework.aop.support.AopUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
;
...
...
@@ -74,7 +77,6 @@ import org.springframework.security.oauth2.provider.approval.ApprovalStoreUserAp
import
org.springframework.security.oauth2.provider.approval.TokenApprovalStore
;
import
org.springframework.security.oauth2.provider.approval.UserApprovalHandler
;
import
org.springframework.security.oauth2.provider.client.BaseClientDetails
;
import
org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService
;
import
org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint
;
import
org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler
;
import
org.springframework.security.oauth2.provider.token.DefaultTokenServices
;
...
...
@@ -90,10 +92,6 @@ import org.springframework.web.client.RestTemplate;
import
com.fasterxml.jackson.databind.JsonNode
;
import
static
org
.
hamcrest
.
CoreMatchers
.
equalTo
;
import
static
org
.
hamcrest
.
CoreMatchers
.
is
;
import
static
org
.
hamcrest
.
MatcherAssert
.
assertThat
;
/**
* Verify Spring Security OAuth2 auto-configuration secures end points properly, accepts
* environmental overrides, and also backs off in the presence of other
...
...
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoTokenServicesTests.java
View file @
af320b49
...
...
@@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
java.util.Collections
;
import
java.util.LinkedHashMap
;
import
java.util.Map
;
...
...
@@ -58,14 +57,14 @@ public class UserInfoTokenServicesTests {
@Test
public
void
sunnyDay
()
{
services
.
setRes
ources
(
Collections
.
singletonMap
(
"foo"
,
template
)
);
services
.
setRes
tTemplate
(
template
);
assertEquals
(
"unknown"
,
services
.
loadAuthentication
(
"FOO"
).
getName
());
}
@Test
public
void
userId
()
{
map
.
put
(
"userid"
,
"spencer"
);
services
.
setRes
ources
(
Collections
.
singletonMap
(
"foo"
,
template
)
);
services
.
setRes
tTemplate
(
template
);
assertEquals
(
"spencer"
,
services
.
loadAuthentication
(
"FOO"
).
getName
());
}
...
...
spring-boot-dependencies/pom.xml
View file @
af320b49
...
...
@@ -136,7 +136,7 @@
<spring-social-linkedin.version>
1.0.1.RELEASE
</spring-social-linkedin.version>
<spring-social-twitter.version>
1.1.0.RELEASE
</spring-social-twitter.version>
<spring-security.version>
3.2.5.RELEASE
</spring-security.version>
<spring-security-oauth2.version>
2.0.
5
.RELEASE
</spring-security-oauth2.version>
<spring-security-oauth2.version>
2.0.
7
.RELEASE
</spring-security-oauth2.version>
<spring-security-jwt.version>
1.0.2.RELEASE
</spring-security-jwt.version>
<spring-ws.version>
2.2.1.RELEASE
</spring-ws.version>
<statsd-client.version>
3.1.0
</statsd-client.version>
...
...
spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc
View file @
af320b49
...
...
@@ -1460,6 +1460,162 @@ All of the above can be switched on and off or modified using external propertie
features add a `@Bean` of type `WebSecurityConfigurerAdapter` with
`@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)`.
=== OAuth2
If you have `spring-security-oauth2` on your classpath you can take advantage of some
autoconfiguration to make it easy to set up Authorization or Resource Server features by
configuring some property values in the `Environment`.
==== Authorization Server
To create an Authorization Server and grant access tokens you need to
`@EnableAuthorizationServer` and provide
`spring.oauth2.client.[clientId,clientSecret]`. The client will be
registered for you in an in-memory repository. To switch off the
autoconfiguration and configure the Authorization Server features
yourself just add a `@Bean` of type
`AuthorizationServerConfigurer`. Having done that you will be able to
usethe client credentials to create an access token, e.g.
----
$ curl client:secret@localhost:8080/oauth/token -d grant_type=password -d username=user -d password=pwd
----
The basic auth credentials for the `/token` endpoint are the client id
and secret, and the user credentials are the normal Spring Security
user details (which default in Spring Boot to "user" and a random
password).
==== Resource Server
To use the access token you need a Resource Server (which can be the
same as the Authorization Server). Creating a Resource Server is easy:
just add `@EnableResourceServer` and provide some configuration to
allow the server to decode access tokens. If your app is also an
Authorization Server it already knows how to decode tokens, so there
is nothing else to do. If your app is a standalone service then you
need to give it some more configuration. Here are the options, one of
the following:
* `spring.oauth2.resource.userInfoUri` to use the "/me" resource
(e.g. "https://uaa.run.pivotal.io/userinfo" on PWS), or
* `spring.oauth2.resource.tokenInfoUri` to use the token decoding endpoint
(e.g. "https://uaa.run.pivotal.io/check_token" on PWS).
If you specify both the `userInfoUri` and the `tokenInfoUri` then
you can set a flag to say that one is preferred over the other
(`preferTokenInfo=true` is the default).
Alternatively (instead of `userInfoUri` or `tokenInfoUri`) if the
tokens are JWTs you can configure a
`spring.oauth2.resource.jwt.keyValue` to decode them locally,
where the key is a verification key. The verification key value is
either a symmetric secret or PEM-encoded RSA public key. If you don't
have the key and it's public you can provide a URI where it can be
downloaded (as a JSON object with a "value" field) with
`spring.oauth2.resource.jwt.keyUri`. E.g. on PWS:
----
$ curl https://uaa.run.pivotal.io/token_key
{"alg":"SHA256withRSA","value":"-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----\n"}
----
WARNING: If you use the `spring.oauth2.resource.jwt.keyUri` the
authorization server needs to be running when your application starts
up. It will log a warning if it can't find the key, and tell you what
to do to fix it.
=== Token Type in User Info
Google (and certain other 3rd party identity providers) is more strict
about the token type name that is sent in the headers to the user info
endpoint. The default is "Bearer" which suits most providers and
matches the spec, but if you need to change it you can set
`spring.oauth2.resource.tokenType`.
=== Customizing the User Info RestTemplate
If you have a `userInfoUri`, the Resource Server features use an
`OAuth2RestTemplate` internally to fetch user details for
authentication. This is provided as a qualified `@Bean` with id
"userInfoRestTemplate", but you shouldn't need to know that to just
use it. The default should be fine for most providers, but
occasionally you might need to add additional interceptors, or change
the request authenticator (which is how the token gets attached to
outgoing requests). To add a customization just create a bean of type
`UserInfoRestTemplateCustomizer` - it has a single method that will be
called after the bean is created but before it is initialized. The
rest template that is being customized here is _only_ used internally
to carry out authentication.
[TIP]
====
To set an RSA key value in YAML use the "pipe" continuation
marker to split it over multiple lines ("|") and remember to indent
the key value (it's a standard YAML language feature). Example:
[source,yaml,indent=0]
----
oauth2:
resource:
jwt:
keyValue: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC...
-----END PUBLIC KEY-----
----
====
==== Client
To make your webapp into an OAuth2 client you can simply
`@EnableOAuth2Client` and Spring Boot will create an
`OAuth2RestTemplate` for you to autowire. It uses the
`spring.oauth2.client.*` as credentials (the same as you might be
using in the Authorization Server), but in addition it will need to
know the authorization and token URIs in the Authorization Server. For
example:
.application.yml
[source,yaml]
----
spring:
oauth2:
client:
clientId: bd1c0a783ccdd1c9b9e4
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
clientAuthenticationScheme: form
resource:
userInfoUri: https://api.github.com/user
preferTokenInfo: false
----
An app with this configuration will redirect to github for
authorization if you attempt to use the `OAuth2RestTemplate`. If you
are already signed into github you won't even notice that it has
authenticated. These specific credentials will only work if your app
is running on port 8080 (register your own client app in Github or
other provider for more flexibility).
To limit the scope that the client asks for when it obtains an access token
you can set `spring.oauth2.client.scope` (comma separated or an array in YAML). By
default the scope is empty and it is up to to Authorization Server to
decide what the defaults should be, usually depending on the settings in
the client registration that it holds.
NOTE: There is also a setting for
`spring.oauth2.client.clientAuthenticationScheme` which defaults to
"header" (but you might need to set it to "form" if, like Github for
instance, your OAuth2 provider doesn't like header authentication). In
fact, the `spring.oauth2.client.*` properties are bound to an instance
of `AuthorizationCodeResourceDetails` so all its properties can be
specified.
=== Actuator Security
If the Actuator is also in use, you will find:
* The management endpoints are secure even if the application endpoints are unsecure.
...
...
spring-boot-samples/spring-boot-sample-secure-oauth2/src/main/java/sample/Application.java
View file @
af320b49
...
...
@@ -16,9 +16,7 @@
package
sample
;
import
org.springframework.boot.SpringApplication
;
import
org.springframework.boot.autoconfigure.EnableAutoConfiguration
;
import
org.springframework.context.annotation.ComponentScan
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.boot.autoconfigure.SpringBootApplication
;
import
org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity
;
import
org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
;
import
org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer
;
...
...
@@ -95,9 +93,7 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E
*/
// @formatter:on
@Configuration
@ComponentScan
@EnableAutoConfiguration
@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@EnableGlobalMethodSecurity
(
prePostEnabled
=
true
)
...
...
spring-boot-samples/spring-boot-sample-secure-oauth2/src/test/java/sample/ApplicationTests.java
View file @
af320b49
package
sample
;
import
static
org
.
hamcrest
.
CoreMatchers
.
is
;
import
static
org
.
hamcrest
.
MatcherAssert
.
assertThat
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
request
.
MockMvcRequestBuilders
.
get
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
request
.
MockMvcRequestBuilders
.
post
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
result
.
MockMvcResultHandlers
.
print
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
result
.
MockMvcResultMatchers
.
header
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
result
.
MockMvcResultMatchers
.
status
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
setup
.
MockMvcBuilders
.
webAppContextSetup
;
import
java.util.Map
;
import
org.junit.Before
;
...
...
@@ -21,14 +30,6 @@ import org.springframework.web.context.WebApplicationContext;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
static
org
.
hamcrest
.
CoreMatchers
.
is
;
import
static
org
.
hamcrest
.
MatcherAssert
.
assertThat
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
request
.
MockMvcRequestBuilders
.
get
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
result
.
MockMvcResultHandlers
.
print
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
result
.
MockMvcResultMatchers
.
header
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
result
.
MockMvcResultMatchers
.
status
;
import
static
org
.
springframework
.
test
.
web
.
servlet
.
setup
.
MockMvcBuilders
.
webAppContextSetup
;
/**
* Series of automated integration tests to verify proper behavior of auto-configured,
* OAuth2-secured system
...
...
@@ -103,20 +104,20 @@ public class ApplicationTests {
@Test
public
void
useAppSecretsPlusUserAccountToGetBearerToken
()
throws
Exception
{
// @formatter:off
MvcResult
result
=
this
.
mvc
.
perform
(
get
(
"/oauth/token"
).
//
post
(
"/oauth/token"
).
header
(
"Authorization"
,
"Basic "
+
new
String
(
Base64
.
encode
(
"foo:bar"
.
getBytes
()))).
//
param
(
"grant_type"
,
"password"
).
//
param
(
"scope"
,
"read"
).
//
param
(
"username"
,
"greg"
).
//
param
(
"password"
,
"turnquist"
)).
//
andExpect
(
status
().
isOk
()).
//
andDo
(
print
()).
//
"Basic "
+
new
String
(
Base64
.
encode
(
"foo:bar"
.
getBytes
()))).
param
(
"grant_type"
,
"password"
).
param
(
"scope"
,
"read"
).
param
(
"username"
,
"greg"
).
param
(
"password"
,
"turnquist"
)).
andExpect
(
status
().
isOk
()).
andDo
(
print
()).
andReturn
();
// @formatter:on
Object
accessToken
=
this
.
objectMapper
.
readValue
(
result
.
getResponse
().
getContentAsString
(),
Map
.
class
).
get
(
"access_token"
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment