Skip to content

Commit 062db0b

Browse files
committed
GH-1111 Fix filter registration for serverless-web
Resolves #1111
1 parent 0b08e8b commit 062db0b

File tree

5 files changed

+96
-23
lines changed

5 files changed

+96
-23
lines changed

spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessAutoConfiguration.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@
1616

1717
package org.springframework.cloud.function.serverless.web;
1818

19-
import jakarta.servlet.Filter;
2019
import org.apache.commons.logging.Log;
2120
import org.apache.commons.logging.LogFactory;
2221

2322
import org.springframework.beans.BeansException;
2423
import org.springframework.beans.factory.InitializingBean;
2524
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
25+
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
2626
import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
2727
import org.springframework.boot.web.server.WebServer;
2828
import org.springframework.boot.web.server.WebServerException;
2929
import org.springframework.boot.web.servlet.ServletContextInitializer;
30+
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
3031
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
3132
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
3233
import org.springframework.cloud.function.serverless.web.ServerlessMVC.ProxyServletConfig;
@@ -96,9 +97,11 @@ public void afterPropertiesSet() throws Exception {
9697
logger.info("Configuring Serverless Web Container");
9798
ServerlessServletContext servletContext = new ServerlessServletContext();
9899
servletApplicationContet.setServletContext(servletContext);
99-
this.applicationContext.getBeansOfType(Filter.class).entrySet().forEach(entry -> {
100-
servletContext.addFilter(entry.getKey(), entry.getValue());
101-
});
100+
for (ServletContextInitializer beans : new ServletContextInitializerBeans(this.applicationContext)) {
101+
if (!(beans instanceof DispatcherServletRegistrationBean)) {
102+
beans.onStartup(servletContext);
103+
}
104+
}
102105
}
103106
}
104107
}

spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletRequest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ public ServerlessHttpServletRequest(ServletContext servletContext, String method
169169
this.servletContext = servletContext;
170170
this.method = method;
171171
this.requestURI = requestURI;
172+
this.pathInfo = requestURI;
172173
this.locales.add(Locale.ENGLISH);
173174
}
174175

spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
import org.junit.jupiter.api.BeforeEach;
2626
import org.junit.jupiter.api.Test;
2727

28-
2928
import org.springframework.cloud.function.test.app.Pet;
3029
import org.springframework.cloud.function.test.app.PetStoreSpringAppConfig;
30+
import org.springframework.cloud.function.test.app.PetStoreSpringAppConfig.AnotherFilter;
31+
import org.springframework.cloud.function.test.app.PetStoreSpringAppConfig.SimpleFilter;
3132
import org.springframework.http.HttpStatus;
32-
import org.springframework.security.test.context.support.WithMockUser;
3333

3434
import static org.assertj.core.api.Assertions.assertThat;
3535

@@ -47,6 +47,8 @@ public class RequestResponseTests {
4747
@BeforeEach
4848
public void before() {
4949
System.setProperty("spring.main.banner-mode", "off");
50+
System.setProperty("trace", "true");
51+
System.setProperty("contextInitTimeout", "20000");
5052
this.mvc = ServerlessMVC.INSTANCE(PetStoreSpringAppConfig.class);
5153
}
5254

@@ -57,11 +59,15 @@ public void after() {
5759

5860
@Test
5961
public void validateAccessDeniedWithCustomHandler() throws Exception {
60-
HttpServletRequest request = new ServerlessHttpServletRequest(null, "GET", "/foo");
62+
HttpServletRequest request = new ServerlessHttpServletRequest(null, "GET", "/foo/deny");
6163
ServerlessHttpServletResponse response = new ServerlessHttpServletResponse();
6264
mvc.service(request, response);
6365
assertThat(response.getErrorMessage()).isEqualTo("Can't touch this");
6466
assertThat(response.getStatus()).isEqualTo(403);
67+
SimpleFilter simpleFilter = this.mvc.getApplicationContext().getBean(SimpleFilter.class);
68+
assertThat(simpleFilter.invoked).isTrue();
69+
AnotherFilter anotherFilter = this.mvc.getApplicationContext().getBean(AnotherFilter.class);
70+
assertThat(anotherFilter.invoked).isTrue();
6571
}
6672

6773
@Test
@@ -89,7 +95,7 @@ public void validateGetListOfPojosWithParam() throws Exception {
8995
assertThat(pets.get(0)).isInstanceOf(Pet.class);
9096
}
9197

92-
@WithMockUser("spring")
98+
//@WithMockUser("spring")
9399
@Test
94100
public void validateGetPojo() throws Exception {
95101
HttpServletRequest request = new ServerlessHttpServletRequest(null, "GET", "/pets/6e3cc370-892f-4efe-a9eb-82926ff8cc5b");

spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import jakarta.servlet.http.HttpSession;
2929

3030
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
31+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
3132
import org.springframework.context.annotation.Bean;
3233
import org.springframework.context.annotation.Configuration;
3334
import org.springframework.context.annotation.Import;
@@ -40,8 +41,11 @@
4041
import org.springframework.security.core.context.SecurityContextHolder;
4142
import org.springframework.security.web.SecurityFilterChain;
4243
import org.springframework.security.web.access.AccessDeniedHandler;
44+
import org.springframework.security.web.authentication.logout.LogoutFilter;
4345
import org.springframework.security.web.context.SecurityContextHolderFilter;
46+
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
4447
import org.springframework.web.filter.GenericFilterBean;
48+
import org.springframework.web.filter.OncePerRequestFilter;
4549
import org.springframework.web.servlet.HandlerAdapter;
4650
import org.springframework.web.servlet.HandlerMapping;
4751
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@@ -73,38 +77,96 @@ public HandlerAdapter handlerAdapter() {
7377
}
7478

7579
@Bean
76-
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
80+
public SecurityFilterChain securityFilterChain(HttpSecurity http, SimpleFilter simpleFilter,
81+
AnotherFilter anotherFilter) throws Exception {
7782
http
83+
.csrf(csrf -> csrf.disable())
84+
.cors(cors -> cors.disable())
7885
.addFilterBefore(new GenericFilterBean() {
7986
@Override
8087
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
8188
throws IOException, ServletException {
8289
SecurityContext securityContext = SecurityContextHolder.getContext();
8390
securityContext.setAuthentication(UsernamePasswordAuthenticationToken.authenticated("user", "password",
84-
Collections.singleton(new SimpleGrantedAuthority("USER"))));
91+
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))));
8592
HttpSession session = ((HttpServletRequest) request).getSession();
8693
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
8794
chain.doFilter(request, response);
8895
}
8996
}, SecurityContextHolderFilter.class)
90-
.securityMatcher("/foo")
91-
.authorizeHttpRequests(authorize -> authorize
92-
.anyRequest().hasRole("FOO")
93-
)
94-
.exceptionHandling(f -> f.accessDeniedHandler(accessDeniedHandler()));
97+
.securityMatcher("/foo/deny")
98+
.authorizeHttpRequests(auth -> {
99+
auth.anyRequest().hasRole("FOO");
100+
})
101+
.addFilterAfter(simpleFilter, LogoutFilter.class)
102+
.addFilterAfter(anotherFilter, RequestCacheAwareFilter.class)
103+
.exceptionHandling(f -> f.accessDeniedHandler(new MyAccessDeinedHandler()));
95104
return http.build();
96105
}
97106

98107
@Bean
99-
public AccessDeniedHandler accessDeniedHandler() {
108+
public FilterRegistrationBean<SimpleFilter> simpleFilterRegistration(SimpleFilter simpleFilter) {
109+
FilterRegistrationBean<SimpleFilter> registration = new FilterRegistrationBean<>(simpleFilter);
110+
registration.setEnabled(false);
111+
return registration;
112+
}
100113

101-
return new AccessDeniedHandler() {
102-
@Override
103-
public void handle(HttpServletRequest request, HttpServletResponse response,
104-
AccessDeniedException accessDeniedException) throws IOException, ServletException {
105-
response.sendError(403, "Can't touch this");
114+
@Bean
115+
public FilterRegistrationBean<AnotherFilter> anotherFilterRegistration(AnotherFilter simpleFilter) {
116+
FilterRegistrationBean<AnotherFilter> registration = new FilterRegistrationBean<>(simpleFilter);
117+
registration.setEnabled(false);
118+
return registration;
119+
}
120+
121+
@Bean
122+
public SimpleFilter simpleFilter() {
123+
return new SimpleFilter();
124+
}
125+
126+
@Bean
127+
public AnotherFilter anotherFilter() {
128+
return new AnotherFilter();
129+
}
130+
131+
public static class SimpleFilter extends OncePerRequestFilter {
132+
public boolean invoked;
133+
134+
@Override
135+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
136+
FilterChain filterChain) throws ServletException, IOException {
137+
if (invoked) {
138+
throw new IllegalStateException("Filter has already been invoked");
139+
}
140+
else {
141+
invoked = true;
142+
}
143+
144+
filterChain.doFilter(request, response);
145+
}
146+
}
147+
148+
public static class AnotherFilter extends OncePerRequestFilter {
149+
public boolean invoked;
150+
@Override
151+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
152+
FilterChain filterChain) throws ServletException, IOException {
153+
if (invoked) {
154+
throw new IllegalStateException("Filter has already been invoked");
155+
}
156+
else {
157+
invoked = true;
106158
}
107-
};
159+
filterChain.doFilter(request, response);
160+
}
108161
}
109162

163+
public static class MyAccessDeinedHandler implements AccessDeniedHandler {
164+
165+
@Override
166+
public void handle(HttpServletRequest request, HttpServletResponse response,
167+
AccessDeniedException accessDeniedException) throws IOException, ServletException {
168+
response.sendError(403, "Can't touch this");
169+
}
170+
171+
}
110172
}

spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetsController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.UUID;
2222

2323
import org.springframework.http.HttpStatus;
24+
import org.springframework.web.bind.annotation.GetMapping;
2425
import org.springframework.web.bind.annotation.PathVariable;
2526
import org.springframework.web.bind.annotation.RequestBody;
2627
import org.springframework.web.bind.annotation.RequestMapping;
@@ -94,7 +95,7 @@ public Pet listPets(@PathVariable String petId) {
9495
return newPet;
9596
}
9697

97-
@RequestMapping(path = "/foo", method = RequestMethod.GET)
98+
@GetMapping("/foo/deny")
9899
public Pet foo() {
99100
Pet newPet = new Pet();
100101
newPet.setId(UUID.randomUUID().toString());

0 commit comments

Comments
 (0)