Skip to content

Commit fe344df

Browse files
author
Dave Syer
committed
Change default order of OAuth2 resource server filter chain
The default is now SecurityProperties.ACCESS_OVERRIDE_ORDER-1 (instead of 3), and the user can set it with security.oauth2.resource.filter-order (as opposed to being hard coded). The filter is provided by Spring OAuth2 so this change is a BeanPostProcessor to call a setter on that object. Fixes spring-projectsgh-5072
1 parent ad4a53e commit fe344df

File tree

11 files changed

+272
-5
lines changed

11 files changed

+272
-5
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerConfiguration.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package org.springframework.boot.autoconfigure.security.oauth2.resource;
1818

1919
import org.springframework.beans.BeanUtils;
20+
import org.springframework.beans.BeansException;
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.beans.factory.config.BeanPostProcessor;
2023
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
2124
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
2225
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -45,6 +48,7 @@
4548
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
4649
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
4750
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
51+
import org.springframework.stereotype.Component;
4852
import org.springframework.util.ClassUtils;
4953
import org.springframework.util.StringUtils;
5054

@@ -98,6 +102,36 @@ public void configure(HttpSecurity http) throws Exception {
98102

99103
}
100104

105+
@Component
106+
public static class ResourceServerFilterChainOrderProcessor
107+
implements BeanPostProcessor {
108+
109+
private ResourceServerProperties resource;
110+
111+
@Autowired
112+
public ResourceServerFilterChainOrderProcessor(
113+
ResourceServerProperties resource) {
114+
this.resource = resource;
115+
}
116+
117+
@Override
118+
public Object postProcessBeforeInitialization(Object bean, String beanName)
119+
throws BeansException {
120+
return bean;
121+
}
122+
123+
@Override
124+
public Object postProcessAfterInitialization(Object bean, String beanName)
125+
throws BeansException {
126+
if (bean instanceof ResourceServerConfiguration) {
127+
ResourceServerConfiguration config = (ResourceServerConfiguration) bean;
128+
config.setOrder(this.resource.getFilterOrder());
129+
}
130+
return bean;
131+
}
132+
133+
}
134+
101135
protected static class ResourceServerCondition extends SpringBootCondition
102136
implements ConfigurationCondition {
103137

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerProperties.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.beans.factory.BeanFactoryAware;
2424
import org.springframework.beans.factory.BeanFactoryUtils;
2525
import org.springframework.beans.factory.ListableBeanFactory;
26+
import org.springframework.boot.autoconfigure.security.SecurityProperties;
2627
import org.springframework.boot.context.properties.ConfigurationProperties;
2728
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
2829
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
@@ -77,6 +78,12 @@ public class ResourceServerProperties implements Validator, BeanFactoryAware {
7778

7879
private Jwt jwt = new Jwt();
7980

81+
/**
82+
* The order of the filter chain used to authenticate tokens. Default puts it after
83+
* the actuator endpoints and before the default HTTP basic filter chain (catchall).
84+
*/
85+
private int filterOrder = SecurityProperties.ACCESS_OVERRIDE_ORDER - 1;
86+
8087
public ResourceServerProperties() {
8188
this(null, null);
8289
}
@@ -159,6 +166,14 @@ public String getClientSecret() {
159166
return this.clientSecret;
160167
}
161168

169+
public int getFilterOrder() {
170+
return this.filterOrder;
171+
}
172+
173+
public void setFilterOrder(int filterOrder) {
174+
this.filterOrder = filterOrder;
175+
}
176+
162177
@Override
163178
public boolean supports(Class<?> clazz) {
164179
return ResourceServerProperties.class.isAssignableFrom(clazz);

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/OAuth2AutoConfigurationTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
import java.util.List;
2222

2323
import com.fasterxml.jackson.databind.JsonNode;
24+
2425
import org.junit.Test;
2526

2627
import org.springframework.aop.support.AopUtils;
2728
import org.springframework.beans.factory.annotation.Autowired;
2829
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.security.SecurityProperties;
2931
import org.springframework.boot.autoconfigure.security.oauth2.authserver.OAuth2AuthorizationServerConfiguration;
3032
import org.springframework.boot.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration;
3133
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration;
@@ -43,6 +45,7 @@
4345
import org.springframework.context.annotation.Bean;
4446
import org.springframework.context.annotation.Configuration;
4547
import org.springframework.context.annotation.Import;
48+
import org.springframework.core.annotation.Order;
4649
import org.springframework.http.HttpEntity;
4750
import org.springframework.http.HttpHeaders;
4851
import org.springframework.http.HttpMethod;
@@ -437,6 +440,7 @@ protected static class MinimalSecureNonWebApplication {
437440
}
438441

439442
@Configuration
443+
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
440444
protected static class TestSecurityConfiguration
441445
extends WebSecurityConfigurerAdapter {
442446

spring-boot-dependencies/pom.xml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
1+
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
42
<modelVersion>4.0.0</modelVersion>
53
<groupId>org.springframework.boot</groupId>
64
<artifactId>spring-boot-dependencies</artifactId>
@@ -2656,4 +2654,4 @@
26562654
<id>integration-test</id>
26572655
</profile>
26582656
</profiles>
2659-
</project>
2657+
</project>

spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2528,6 +2528,10 @@ WARNING: If you use the `security.oauth2.resource.jwt.key-uri` the authorization
25282528
needs to be running when your application starts up. It will log a warning if it can't
25292529
find the key, and tell you what to do to fix it.
25302530

2531+
OAuth2 resources are protected by a filter chain with order
2532+
`security.oauth2.resource.filter-order` and the default is after the
2533+
filter protecting the actuator endpoints by default (so actuator
2534+
endpoints will stay on HTTP Basic unless you change the order).
25312535

25322536

25332537
[[boot-features-security-oauth2-token-type]]

spring-boot-samples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
<module>spring-boot-sample-property-validation</module>
7979
<module>spring-boot-sample-secure</module>
8080
<module>spring-boot-sample-secure-oauth2</module>
81+
<module>spring-boot-sample-secure-oauth2-actuator</module>
8182
<module>spring-boot-sample-secure-oauth2-resource</module>
8283
<module>spring-boot-sample-servlet</module>
8384
<module>spring-boot-sample-session-redis</module>

spring-boot-samples/spring-boot-sample-ant/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,4 @@
102102
</plugin>
103103
</plugins>
104104
</build>
105-
</project>
105+
</project>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<!-- Your own application should inherit from spring-boot-starter-parent -->
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-samples</artifactId>
8+
<version>1.5.0.BUILD-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>spring-boot-sample-secure-oauth2-actuator</artifactId>
11+
<name>spring-boot-sample-secure-oauth2-actuator</name>
12+
<description>Spring Boot Security OAuth2 Actuator Sample</description>
13+
<url>http://projects.spring.io/spring-boot/</url>
14+
<organization>
15+
<name>Pivotal Software, Inc.</name>
16+
<url>http://www.spring.io</url>
17+
</organization>
18+
<properties>
19+
<main.basedir>${basedir}/../..</main.basedir>
20+
</properties>
21+
<dependencies>
22+
<!-- Compile -->
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter-security</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.springframework.boot</groupId>
29+
<artifactId>spring-boot-starter-web</artifactId>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.springframework.boot</groupId>
33+
<artifactId>spring-boot-starter-actuator</artifactId>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.springframework.security.oauth</groupId>
37+
<artifactId>spring-security-oauth2</artifactId>
38+
</dependency>
39+
<!-- Test -->
40+
<dependency>
41+
<groupId>org.springframework.boot</groupId>
42+
<artifactId>spring-boot-starter-test</artifactId>
43+
<scope>test</scope>
44+
</dependency>
45+
</dependencies>
46+
<build>
47+
<plugins>
48+
<plugin>
49+
<groupId>org.springframework.boot</groupId>
50+
<artifactId>spring-boot-maven-plugin</artifactId>
51+
</plugin>
52+
</plugins>
53+
</build>
54+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2012-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 sample.secure.oauth2.resource;
18+
19+
import java.util.UUID;
20+
21+
import org.springframework.boot.SpringApplication;
22+
import org.springframework.boot.autoconfigure.SpringBootApplication;
23+
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
24+
import org.springframework.web.bind.annotation.GetMapping;
25+
import org.springframework.web.bind.annotation.RestController;
26+
27+
@SpringBootApplication
28+
@EnableResourceServer
29+
@RestController
30+
public class SampleSecureOAuth2ResourceApplication {
31+
32+
@GetMapping("/")
33+
public Message home() {
34+
return new Message("Hello World");
35+
}
36+
37+
public static void main(String[] args) {
38+
SpringApplication.run(SampleSecureOAuth2ResourceApplication.class, args);
39+
}
40+
41+
}
42+
43+
class Message {
44+
45+
private String id = UUID.randomUUID().toString();
46+
47+
private String value;
48+
49+
public Message(String value) {
50+
this.value = value;
51+
}
52+
53+
public String getId() {
54+
return id;
55+
}
56+
57+
public String getValue() {
58+
return value;
59+
}
60+
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
server.port=8081
2+
security.basic.enabled=true
3+
security.user.password=password
4+
security.oauth2.resource.id=service
5+
security.oauth2.resource.userInfoUri=http://localhost:8080/user
6+
logging.level.org.springframework.security=DEBUG
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2012-2016 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 sample.secure.oauth2.resource;
18+
19+
import org.junit.Before;
20+
import org.junit.Test;
21+
import org.junit.runner.RunWith;
22+
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.boot.test.context.SpringBootTest;
25+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
26+
import org.springframework.security.core.context.SecurityContextHolder;
27+
import org.springframework.security.web.FilterChainProxy;
28+
import org.springframework.test.context.junit4.SpringRunner;
29+
import org.springframework.test.web.servlet.MockMvc;
30+
import org.springframework.util.Base64Utils;
31+
import org.springframework.web.context.WebApplicationContext;
32+
33+
import static org.hamcrest.CoreMatchers.containsString;
34+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
35+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
36+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
37+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
38+
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
39+
40+
/**
41+
* Series of automated integration tests to verify proper behavior of auto-configured,
42+
* OAuth2-secured system
43+
*
44+
* @author Dave Syer
45+
*/
46+
@RunWith(SpringRunner.class)
47+
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
48+
public class SampleSecureOAuth2ResourceApplicationTests {
49+
50+
@Autowired
51+
WebApplicationContext context;
52+
53+
@Autowired
54+
FilterChainProxy filterChain;
55+
56+
private MockMvc mvc;
57+
58+
@Before
59+
public void setUp() {
60+
this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build();
61+
SecurityContextHolder.clearContext();
62+
}
63+
64+
@Test
65+
public void homePageSecuredByDefault() throws Exception {
66+
this.mvc.perform(get("/")).andExpect(status().isUnauthorized())
67+
.andExpect(header().string("WWW-Authenticate", containsString("Bearer")))
68+
.andDo(print());
69+
}
70+
71+
@Test
72+
public void healthAvailable() throws Exception {
73+
this.mvc.perform(get("/health")).andExpect(status().isOk()).andDo(print());
74+
}
75+
76+
@Test
77+
public void envSecuredWithBasic() throws Exception {
78+
this.mvc.perform(get("/env")).andExpect(status().isUnauthorized())
79+
.andExpect(header().string("WWW-Authenticate", containsString("Basic")))
80+
.andDo(print());
81+
}
82+
83+
@Test
84+
public void envWithPassword() throws Exception {
85+
this.mvc.perform(get("/env").header("Authorization",
86+
"Basic " + Base64Utils.encodeToString("user:password".getBytes())))
87+
.andExpect(status().isOk()).andDo(print());
88+
}
89+
90+
}

0 commit comments

Comments
 (0)