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
b4134e23
Commit
b4134e23
authored
Feb 15, 2017
by
Madhura Bhave
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add autoconfiguration for JWKTokenStore
If `jwk.key-set-uri` is present. Closes gh-4437
parent
01729cc1
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
225 additions
and
39 deletions
+225
-39
ResourceServerProperties.java
...re/security/oauth2/resource/ResourceServerProperties.java
+58
-18
ResourceServerTokenServicesConfiguration.java
...h2/resource/ResourceServerTokenServicesConfiguration.java
+59
-8
ResourceServerPropertiesTests.java
...curity/oauth2/resource/ResourceServerPropertiesTests.java
+40
-0
ResourceServerTokenServicesConfigurationTests.java
...source/ResourceServerTokenServicesConfigurationTests.java
+21
-0
pom.xml
spring-boot-dependencies/pom.xml
+1
-1
BindFailureAnalyzer.java
...mework/boot/diagnostics/analyzer/BindFailureAnalyzer.java
+12
-7
BindFailureAnalyzerTests.java
...k/boot/diagnostics/analyzer/BindFailureAnalyzerTests.java
+34
-5
No files found.
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java
View file @
b4134e23
...
@@ -35,6 +35,7 @@ import org.springframework.validation.Validator;
...
@@ -35,6 +35,7 @@ import org.springframework.validation.Validator;
* Configuration properties for OAuth2 Resources.
* Configuration properties for OAuth2 Resources.
*
*
* @author Dave Syer
* @author Dave Syer
* @author Madhura Bhave
* @since 1.3.0
* @since 1.3.0
*/
*/
@ConfigurationProperties
(
prefix
=
"security.oauth2.resource"
)
@ConfigurationProperties
(
prefix
=
"security.oauth2.resource"
)
...
@@ -78,6 +79,8 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
...
@@ -78,6 +79,8 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
private
Jwt
jwt
=
new
Jwt
();
private
Jwt
jwt
=
new
Jwt
();
private
Jwk
jwk
=
new
Jwk
();
/**
/**
* The order of the filter chain used to authenticate tokens. Default puts it after
* The order of the filter chain used to authenticate tokens. Default puts it after
* the actuator endpoints and before the default HTTP basic filter chain (catchall).
* the actuator endpoints and before the default HTTP basic filter chain (catchall).
...
@@ -158,6 +161,14 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
...
@@ -158,6 +161,14 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
this
.
jwt
=
jwt
;
this
.
jwt
=
jwt
;
}
}
public
Jwk
getJwk
()
{
return
this
.
jwk
;
}
public
void
setJwk
(
Jwk
jwk
)
{
this
.
jwk
=
jwk
;
}
public
String
getClientId
()
{
public
String
getClientId
()
{
return
this
.
clientId
;
return
this
.
clientId
;
}
}
...
@@ -192,29 +203,40 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
...
@@ -192,29 +203,40 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
return
;
return
;
}
}
ResourceServerProperties
resource
=
(
ResourceServerProperties
)
target
;
ResourceServerProperties
resource
=
(
ResourceServerProperties
)
target
;
if
(
StringUtils
.
hasText
(
this
.
clientId
))
{
if
(!
StringUtils
.
hasText
(
this
.
clientSecret
))
{
if
((
StringUtils
.
hasText
(
this
.
jwt
.
getKeyUri
())
if
(!
StringUtils
.
hasText
(
resource
.
getUserInfoUri
()))
{
||
StringUtils
.
hasText
(
this
.
jwt
.
getKeyValue
()))
errors
.
rejectValue
(
"userInfoUri"
,
"missing.userInfoUri"
,
&&
StringUtils
.
hasText
(
this
.
jwk
.
getKeySetUri
()))
{
"Missing userInfoUri (no client secret available)"
);
errors
.
reject
(
"ambiguous.keyUri"
,
"Only one of jwt.keyUri (or jwt.keyValue) and jwk.keySetUri should be configured."
);
}
}
}
else
{
else
{
if
(
isPreferTokenInfo
()
if
(
StringUtils
.
hasText
(
this
.
clientId
))
{
&&
!
StringUtils
.
hasText
(
resource
.
getTokenInfoUri
()))
{
if
(!
StringUtils
.
hasText
(
this
.
clientSecret
))
{
if
(
StringUtils
.
hasText
(
getJwt
().
getKeyUri
())
||
StringUtils
.
hasText
(
getJwt
().
getKeyValue
()))
{
// It's a JWT decoder
return
;
}
if
(!
StringUtils
.
hasText
(
resource
.
getUserInfoUri
()))
{
if
(!
StringUtils
.
hasText
(
resource
.
getUserInfoUri
()))
{
errors
.
rejectValue
(
"tokenInfoUri"
,
"missing.tokenInfoUri"
,
errors
.
rejectValue
(
"userInfoUri"
,
"missing.userInfoUri"
,
"Missing tokenInfoUri and userInfoUri and there is no "
"Missing userInfoUri (no client secret available)"
);
+
"JWT verifier key"
);
}
}
else
{
if
(
isPreferTokenInfo
()
&&
!
StringUtils
.
hasText
(
resource
.
getTokenInfoUri
()))
{
if
(
StringUtils
.
hasText
(
getJwt
().
getKeyUri
())
||
StringUtils
.
hasText
(
getJwt
().
getKeyValue
())
||
StringUtils
.
hasText
(
getJwk
().
getKeySetUri
()))
{
// It's a JWT decoder
return
;
}
if
(!
StringUtils
.
hasText
(
resource
.
getUserInfoUri
()))
{
errors
.
rejectValue
(
"tokenInfoUri"
,
"missing.tokenInfoUri"
,
"Missing tokenInfoUri and userInfoUri and there is no "
+
"JWT verifier key"
);
}
}
}
}
}
}
}
}
}
}
}
private
int
countBeans
(
Class
<?>
type
)
{
private
int
countBeans
(
Class
<?>
type
)
{
...
@@ -269,4 +291,22 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
...
@@ -269,4 +291,22 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
}
}
public
class
Jwk
{
/**
* The URI to get verification keys to verify the JWT token.
* This can be set when the authorization server returns a
* set of verification keys.
*/
private
String
keySetUri
;
public
String
getKeySetUri
()
{
return
this
.
keySetUri
;
}
public
void
setKeySetUri
(
String
keySetUri
)
{
this
.
keySetUri
=
keySetUri
;
}
}
}
}
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java
View file @
b4134e23
...
@@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
...
@@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnClass
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnClass
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass
;
import
org.springframework.boot.autoconfigure.condition.NoneNestedConditions
;
import
org.springframework.boot.autoconfigure.condition.SpringBootCondition
;
import
org.springframework.boot.autoconfigure.condition.SpringBootCondition
;
import
org.springframework.boot.bind.RelaxedPropertyResolver
;
import
org.springframework.boot.bind.RelaxedPropertyResolver
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Bean
;
...
@@ -61,6 +62,7 @@ import org.springframework.security.oauth2.provider.token.ResourceServerTokenSer
...
@@ -61,6 +62,7 @@ import org.springframework.security.oauth2.provider.token.ResourceServerTokenSer
import
org.springframework.security.oauth2.provider.token.TokenStore
;
import
org.springframework.security.oauth2.provider.token.TokenStore
;
import
org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter
;
import
org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter
;
import
org.springframework.security.oauth2.provider.token.store.JwtTokenStore
;
import
org.springframework.security.oauth2.provider.token.store.JwtTokenStore
;
import
org.springframework.security.oauth2.provider.token.store.jwk.JwkTokenStore
;
import
org.springframework.social.connect.ConnectionFactoryLocator
;
import
org.springframework.social.connect.ConnectionFactoryLocator
;
import
org.springframework.social.connect.support.OAuth2ConnectionFactory
;
import
org.springframework.social.connect.support.OAuth2ConnectionFactory
;
import
org.springframework.util.CollectionUtils
;
import
org.springframework.util.CollectionUtils
;
...
@@ -73,6 +75,7 @@ import org.springframework.web.client.RestTemplate;
...
@@ -73,6 +75,7 @@ import org.springframework.web.client.RestTemplate;
* Configuration for an OAuth2 resource server.
* Configuration for an OAuth2 resource server.
*
*
* @author Dave Syer
* @author Dave Syer
* @author Madhura Bhave
* @since 1.3.0
* @since 1.3.0
*/
*/
@Configuration
@Configuration
...
@@ -93,7 +96,7 @@ public class ResourceServerTokenServicesConfiguration {
...
@@ -93,7 +96,7 @@ public class ResourceServerTokenServicesConfiguration {
}
}
@Configuration
@Configuration
@Conditional
(
NotJwt
TokenCondition
.
class
)
@Conditional
(
Remote
TokenCondition
.
class
)
protected
static
class
RemoteTokenServicesConfiguration
{
protected
static
class
RemoteTokenServicesConfiguration
{
@Configuration
@Configuration
...
@@ -214,6 +217,30 @@ public class ResourceServerTokenServicesConfiguration {
...
@@ -214,6 +217,30 @@ public class ResourceServerTokenServicesConfiguration {
}
}
@Configuration
@Conditional
(
JwkCondition
.
class
)
protected
static
class
JwkTokenStoreConfiguration
{
private
final
ResourceServerProperties
resource
;
public
JwkTokenStoreConfiguration
(
ResourceServerProperties
resource
)
{
this
.
resource
=
resource
;
}
@Bean
@ConditionalOnMissingBean
(
ResourceServerTokenServices
.
class
)
public
DefaultTokenServices
jwkTokenServices
()
{
DefaultTokenServices
services
=
new
DefaultTokenServices
();
services
.
setTokenStore
(
jwkTokenStore
());
return
services
;
}
@Bean
public
TokenStore
jwkTokenStore
()
{
return
new
JwkTokenStore
(
this
.
resource
.
getJwk
().
getKeySetUri
());
}
}
@Configuration
@Configuration
@Conditional
(
JwtTokenCondition
.
class
)
@Conditional
(
JwtTokenCondition
.
class
)
protected
static
class
JwtTokenServicesConfiguration
{
protected
static
class
JwtTokenServicesConfiguration
{
...
@@ -341,32 +368,56 @@ public class ResourceServerTokenServicesConfiguration {
...
@@ -341,32 +368,56 @@ public class ResourceServerTokenServicesConfiguration {
}
}
private
static
class
NotTokenInfoCondition
extends
SpringBootCondition
{
private
static
class
JwkCondition
extends
SpringBootCondition
{
private
TokenInfoCondition
tokenInfoCondition
=
new
TokenInfoCondition
();
@Override
@Override
public
ConditionOutcome
getMatchOutcome
(
ConditionContext
context
,
public
ConditionOutcome
getMatchOutcome
(
ConditionContext
context
,
AnnotatedTypeMetadata
metadata
)
{
AnnotatedTypeMetadata
metadata
)
{
ConditionMessage
.
Builder
message
=
ConditionMessage
.
forCondition
(
"OAuth JWK Condition"
);
RelaxedPropertyResolver
resolver
=
new
RelaxedPropertyResolver
(
context
.
getEnvironment
(),
"security.oauth2.resource.jwk."
);
String
keyUri
=
resolver
.
getProperty
(
"key-set-uri"
);
if
(
StringUtils
.
hasText
(
keyUri
))
{
return
ConditionOutcome
.
match
(
message
.
foundExactly
(
"provided jwk key set URI"
));
}
return
ConditionOutcome
return
ConditionOutcome
.
inverse
(
this
.
tokenInfoCondition
.
getMatchOutcome
(
context
,
metadata
));
.
noMatch
(
message
.
didNotFind
(
"key jwk set URI not provided"
).
atAll
(
));
}
}
}
}
private
static
class
Not
JwtToken
Condition
extends
SpringBootCondition
{
private
static
class
Not
TokenInfo
Condition
extends
SpringBootCondition
{
private
JwtTokenCondition
jwtTokenCondition
=
new
JwtToken
Condition
();
private
TokenInfoCondition
tokenInfoCondition
=
new
TokenInfo
Condition
();
@Override
@Override
public
ConditionOutcome
getMatchOutcome
(
ConditionContext
context
,
public
ConditionOutcome
getMatchOutcome
(
ConditionContext
context
,
AnnotatedTypeMetadata
metadata
)
{
AnnotatedTypeMetadata
metadata
)
{
return
ConditionOutcome
return
ConditionOutcome
.
inverse
(
this
.
jwtToken
Condition
.
getMatchOutcome
(
context
,
metadata
));
.
inverse
(
this
.
tokenInfo
Condition
.
getMatchOutcome
(
context
,
metadata
));
}
}
}
}
private
static
class
RemoteTokenCondition
extends
NoneNestedConditions
{
RemoteTokenCondition
()
{
super
(
ConfigurationPhase
.
PARSE_CONFIGURATION
);
}
@Conditional
(
JwtTokenCondition
.
class
)
static
class
HasJwtConfiguration
{
}
@Conditional
(
JwkCondition
.
class
)
static
class
HasJwkConfiguration
{
}
}
static
class
AcceptJsonRequestInterceptor
implements
ClientHttpRequestInterceptor
{
static
class
AcceptJsonRequestInterceptor
implements
ClientHttpRequestInterceptor
{
@Override
@Override
...
...
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerPropertiesTests.java
View file @
b4134e23
...
@@ -21,13 +21,21 @@ import java.util.Map;
...
@@ -21,13 +21,21 @@ import java.util.Map;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.springframework.beans.factory.ListableBeanFactory
;
import
org.springframework.validation.Errors
;
import
org.springframework.web.context.support.StaticWebApplicationContext
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
verifyZeroInteractions
;
/**
/**
* Tests for {@link ResourceServerProperties}.
* Tests for {@link ResourceServerProperties}.
*
*
* @author Dave Syer
* @author Dave Syer
* @author Vedran Pavic
* @author Vedran Pavic
* @author Madhura Bhave
*/
*/
public
class
ResourceServerPropertiesTests
{
public
class
ResourceServerPropertiesTests
{
...
@@ -59,4 +67,36 @@ public class ResourceServerPropertiesTests {
...
@@ -59,4 +67,36 @@ public class ResourceServerPropertiesTests {
.
isEqualTo
(
"http://example.com/token_key"
);
.
isEqualTo
(
"http://example.com/token_key"
);
}
}
@Test
public
void
validateWhenBothJwtAndJwtKeyConfigurationPresentShouldFail
()
throws
Exception
{
this
.
properties
.
getJwk
().
setKeySetUri
(
"http://my-auth-server/token_keys"
);
this
.
properties
.
getJwt
().
setKeyUri
(
"http://my-auth-server/token_key"
);
setListableBeanFactory
();
Errors
errors
=
mock
(
Errors
.
class
);
this
.
properties
.
validate
(
this
.
properties
,
errors
);
verify
(
errors
).
reject
(
"ambiguous.keyUri"
,
"Only one of jwt.keyUri (or jwt.keyValue) and jwk.keySetUri should be configured."
);
}
@Test
public
void
validateWhenKeySetUriProvidedShouldSucceed
()
throws
Exception
{
this
.
properties
.
getJwk
().
setKeySetUri
(
"http://my-auth-server/token_keys"
);
setListableBeanFactory
();
Errors
errors
=
mock
(
Errors
.
class
);
this
.
properties
.
validate
(
this
.
properties
,
errors
);
verifyZeroInteractions
(
errors
);
}
private
void
setListableBeanFactory
()
{
ListableBeanFactory
beanFactory
=
new
StaticWebApplicationContext
()
{
@Override
public
String
[]
getBeanNamesForType
(
Class
<?>
type
,
boolean
includeNonSingletons
,
boolean
allowEagerInit
)
{
if
(
type
.
isAssignableFrom
(
ResourceServerTokenServicesConfiguration
.
class
))
{
return
new
String
[]{
"ResourceServerTokenServicesConfiguration"
};
}
return
new
String
[
0
];
}
};
this
.
properties
.
setBeanFactory
(
beanFactory
);
}
}
}
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfigurationTests.java
View file @
b4134e23
...
@@ -21,8 +21,11 @@ import java.util.List;
...
@@ -21,8 +21,11 @@ import java.util.List;
import
java.util.Map
;
import
java.util.Map
;
import
org.junit.After
;
import
org.junit.After
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.junit.rules.ExpectedException
;
import
org.springframework.beans.factory.NoSuchBeanDefinitionException
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
;
import
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
;
...
@@ -61,6 +64,7 @@ import static org.mockito.Mockito.mock;
...
@@ -61,6 +64,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link ResourceServerTokenServicesConfiguration}.
* Tests for {@link ResourceServerTokenServicesConfiguration}.
*
*
* @author Dave Syer
* @author Dave Syer
* @author Madhura Bhave
*/
*/
public
class
ResourceServerTokenServicesConfigurationTests
{
public
class
ResourceServerTokenServicesConfigurationTests
{
...
@@ -76,6 +80,9 @@ public class ResourceServerTokenServicesConfigurationTests {
...
@@ -76,6 +80,9 @@ public class ResourceServerTokenServicesConfigurationTests {
private
ConfigurableEnvironment
environment
=
new
StandardEnvironment
();
private
ConfigurableEnvironment
environment
=
new
StandardEnvironment
();
@Rule
public
ExpectedException
thrown
=
ExpectedException
.
none
();
@After
@After
public
void
close
()
{
public
void
close
()
{
if
(
this
.
context
!=
null
)
{
if
(
this
.
context
!=
null
)
{
...
@@ -186,6 +193,8 @@ public class ResourceServerTokenServicesConfigurationTests {
...
@@ -186,6 +193,8 @@ public class ResourceServerTokenServicesConfigurationTests {
.
environment
(
this
.
environment
).
web
(
false
).
run
();
.
environment
(
this
.
environment
).
web
(
false
).
run
();
DefaultTokenServices
services
=
this
.
context
.
getBean
(
DefaultTokenServices
.
class
);
DefaultTokenServices
services
=
this
.
context
.
getBean
(
DefaultTokenServices
.
class
);
assertThat
(
services
).
isNotNull
();
assertThat
(
services
).
isNotNull
();
this
.
thrown
.
expect
(
NoSuchBeanDefinitionException
.
class
);
this
.
context
.
getBean
(
RemoteTokenServices
.
class
);
}
}
@Test
@Test
...
@@ -198,6 +207,18 @@ public class ResourceServerTokenServicesConfigurationTests {
...
@@ -198,6 +207,18 @@ public class ResourceServerTokenServicesConfigurationTests {
assertThat
(
services
).
isNotNull
();
assertThat
(
services
).
isNotNull
();
}
}
@Test
public
void
jwkConfiguration
()
throws
Exception
{
EnvironmentTestUtils
.
addEnvironment
(
this
.
environment
,
"security.oauth2.resource.jwk.key-set-uri=http://my-auth-server/token_keys"
);
this
.
context
=
new
SpringApplicationBuilder
(
ResourceConfiguration
.
class
)
.
environment
(
this
.
environment
).
web
(
false
).
run
();
DefaultTokenServices
services
=
this
.
context
.
getBean
(
DefaultTokenServices
.
class
);
assertThat
(
services
).
isNotNull
();
this
.
thrown
.
expect
(
NoSuchBeanDefinitionException
.
class
);
this
.
context
.
getBean
(
RemoteTokenServices
.
class
);
}
@Test
@Test
public
void
springSocialUserInfo
()
{
public
void
springSocialUserInfo
()
{
EnvironmentTestUtils
.
addEnvironment
(
this
.
environment
,
EnvironmentTestUtils
.
addEnvironment
(
this
.
environment
,
...
...
spring-boot-dependencies/pom.xml
View file @
b4134e23
...
@@ -168,7 +168,7 @@
...
@@ -168,7 +168,7 @@
<spring-retry.version>
1.2.0.RELEASE
</spring-retry.version>
<spring-retry.version>
1.2.0.RELEASE
</spring-retry.version>
<spring-security.version>
4.2.1.RELEASE
</spring-security.version>
<spring-security.version>
4.2.1.RELEASE
</spring-security.version>
<spring-security-jwt.version>
1.0.7.RELEASE
</spring-security-jwt.version>
<spring-security-jwt.version>
1.0.7.RELEASE
</spring-security-jwt.version>
<spring-security-oauth.version>
2.0.1
2.RELEASE
</spring-security-oauth.version>
<spring-security-oauth.version>
2.0.1
3.BUILD-SNAPSHOT
</spring-security-oauth.version>
<spring-session.version>
1.3.0.RELEASE
</spring-session.version>
<spring-session.version>
1.3.0.RELEASE
</spring-session.version>
<spring-social.version>
1.1.4.RELEASE
</spring-social.version>
<spring-social.version>
1.1.4.RELEASE
</spring-social.version>
<spring-social-facebook.version>
2.0.3.RELEASE
</spring-social-facebook.version>
<spring-social-facebook.version>
2.0.3.RELEASE
</spring-social-facebook.version>
...
...
spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzer.java
View file @
b4134e23
...
@@ -21,29 +21,34 @@ import org.springframework.boot.diagnostics.FailureAnalysis;
...
@@ -21,29 +21,34 @@ import org.springframework.boot.diagnostics.FailureAnalysis;
import
org.springframework.util.CollectionUtils
;
import
org.springframework.util.CollectionUtils
;
import
org.springframework.validation.BindException
;
import
org.springframework.validation.BindException
;
import
org.springframework.validation.FieldError
;
import
org.springframework.validation.FieldError
;
import
org.springframework.validation.ObjectError
;
/**
/**
* An {@link AbstractFailureAnalyzer} that performs analysis of failures caused by a
* An {@link AbstractFailureAnalyzer} that performs analysis of failures caused by a
* {@link BindException}.
* {@link BindException}.
*
*
* @author Andy Wilkinson
* @author Andy Wilkinson
* @author Madhura Bhave
*/
*/
class
BindFailureAnalyzer
extends
AbstractFailureAnalyzer
<
BindException
>
{
class
BindFailureAnalyzer
extends
AbstractFailureAnalyzer
<
BindException
>
{
@Override
@Override
protected
FailureAnalysis
analyze
(
Throwable
rootFailure
,
BindException
cause
)
{
protected
FailureAnalysis
analyze
(
Throwable
rootFailure
,
BindException
cause
)
{
if
(
CollectionUtils
.
isEmpty
(
cause
.
get
Field
Errors
()))
{
if
(
CollectionUtils
.
isEmpty
(
cause
.
get
All
Errors
()))
{
return
null
;
return
null
;
}
}
StringBuilder
description
=
new
StringBuilder
(
StringBuilder
description
=
new
StringBuilder
(
String
.
format
(
"Binding to target %s failed:%n"
,
cause
.
getTarget
()));
String
.
format
(
"Binding to target %s failed:%n"
,
cause
.
getTarget
()));
for
(
FieldError
fieldError
:
cause
.
getFieldErrors
())
{
for
(
ObjectError
error
:
cause
.
getAllErrors
())
{
description
.
append
(
String
.
format
(
"%n Property: %s"
,
if
(
error
instanceof
FieldError
)
{
cause
.
getObjectName
()
+
"."
+
fieldError
.
getField
()));
FieldError
fieldError
=
(
FieldError
)
error
;
description
.
append
(
String
.
format
(
"%n Property: %s"
,
cause
.
getObjectName
()
+
"."
+
fieldError
.
getField
()));
description
.
append
(
String
.
format
(
"%n Value: %s"
,
fieldError
.
getRejectedValue
()));
}
description
.
append
(
description
.
append
(
String
.
format
(
"%n Value: %s"
,
fieldError
.
getRejectedValue
()));
String
.
format
(
"%n Reason: %s%n"
,
error
.
getDefaultMessage
()));
description
.
append
(
String
.
format
(
"%n Reason: %s%n"
,
fieldError
.
getDefaultMessage
()));
}
}
return
new
FailureAnalysis
(
description
.
toString
(),
return
new
FailureAnalysis
(
description
.
toString
(),
"Update your application's configuration"
,
cause
);
"Update your application's configuration"
,
cause
);
...
...
spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzerTests.java
View file @
b4134e23
...
@@ -32,6 +32,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
...
@@ -32,6 +32,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import
org.springframework.boot.diagnostics.FailureAnalysis
;
import
org.springframework.boot.diagnostics.FailureAnalysis
;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext
;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext
;
import
org.springframework.context.i18n.LocaleContextHolder
;
import
org.springframework.context.i18n.LocaleContextHolder
;
import
org.springframework.validation.Errors
;
import
org.springframework.validation.Validator
;
import
org.springframework.validation.annotation.Validated
;
import
org.springframework.validation.annotation.Validated
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
...
@@ -40,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
...
@@ -40,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link BindFailureAnalyzer}.
* Tests for {@link BindFailureAnalyzer}.
*
*
* @author Andy Wilkinson
* @author Andy Wilkinson
* @author Madhura Bhave
*/
*/
public
class
BindFailureAnalyzerTests
{
public
class
BindFailureAnalyzerTests
{
...
@@ -54,8 +57,8 @@ public class BindFailureAnalyzerTests {
...
@@ -54,8 +57,8 @@ public class BindFailureAnalyzerTests {
}
}
@Test
@Test
public
void
bindExceptionDueToValidationFailure
()
{
public
void
bindException
WithFieldErrors
DueToValidationFailure
()
{
FailureAnalysis
analysis
=
performAnalysis
(
ValidationFailureConfiguration
.
class
);
FailureAnalysis
analysis
=
performAnalysis
(
Field
ValidationFailureConfiguration
.
class
);
assertThat
(
analysis
.
getDescription
())
assertThat
(
analysis
.
getDescription
())
.
contains
(
failure
(
"test.foo.foo"
,
"null"
,
"may not be null"
));
.
contains
(
failure
(
"test.foo.foo"
,
"null"
,
"may not be null"
));
assertThat
(
analysis
.
getDescription
())
assertThat
(
analysis
.
getDescription
())
...
@@ -64,6 +67,13 @@ public class BindFailureAnalyzerTests {
...
@@ -64,6 +67,13 @@ public class BindFailureAnalyzerTests {
.
contains
(
failure
(
"test.foo.nested.bar"
,
"null"
,
"may not be null"
));
.
contains
(
failure
(
"test.foo.nested.bar"
,
"null"
,
"may not be null"
));
}
}
@Test
public
void
bindExceptionWithObjectErrorsDueToValidationFailure
()
throws
Exception
{
FailureAnalysis
analysis
=
performAnalysis
(
ObjectValidationFailureConfiguration
.
class
);
assertThat
(
analysis
.
getDescription
())
.
contains
(
"Reason: This object could not be bound."
);
}
private
static
String
failure
(
String
property
,
String
value
,
String
reason
)
{
private
static
String
failure
(
String
property
,
String
value
,
String
reason
)
{
return
String
.
format
(
"Property: %s%n Value: %s%n Reason: %s"
,
property
,
return
String
.
format
(
"Property: %s%n Value: %s%n Reason: %s"
,
property
,
value
,
reason
);
value
,
reason
);
...
@@ -85,14 +95,19 @@ public class BindFailureAnalyzerTests {
...
@@ -85,14 +95,19 @@ public class BindFailureAnalyzerTests {
}
}
}
}
@EnableConfigurationProperties
(
ValidationFailureProperties
.
class
)
@EnableConfigurationProperties
(
FieldValidationFailureProperties
.
class
)
static
class
ValidationFailureConfiguration
{
static
class
FieldValidationFailureConfiguration
{
}
@EnableConfigurationProperties
(
ObjectErrorFailureProperties
.
class
)
static
class
ObjectValidationFailureConfiguration
{
}
}
@ConfigurationProperties
(
"test.foo"
)
@ConfigurationProperties
(
"test.foo"
)
@Validated
@Validated
static
class
ValidationFailureProperties
{
static
class
Field
ValidationFailureProperties
{
@NotNull
@NotNull
private
String
foo
;
private
String
foo
;
...
@@ -144,4 +159,18 @@ public class BindFailureAnalyzerTests {
...
@@ -144,4 +159,18 @@ public class BindFailureAnalyzerTests {
}
}
@ConfigurationProperties
(
"foo.bar"
)
static
class
ObjectErrorFailureProperties
implements
Validator
{
@Override
public
void
validate
(
Object
target
,
Errors
errors
)
{
errors
.
reject
(
"my.objectError"
,
"This object could not be bound."
);
}
@Override
public
boolean
supports
(
Class
<?>
clazz
)
{
return
true
;
}
}
}
}
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