diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml
index 49efdb8a9..f06c9ffc6 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml
@@ -24,8 +24,34 @@
org.springframework
spring-webmvc
-
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.thymeleaf.extras
+ thymeleaf-extras-springsecurity6
+
+ 3.1.1.RELEASE
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
+
jakarta.servlet
jakarta.servlet-api
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletRequest.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletRequest.java
index 5108931c9..574b78c1f 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletRequest.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletRequest.java
@@ -446,7 +446,7 @@ public class ProxyHttpServletRequest implements HttpServletRequest {
@Override
public String getScheme() {
- throw new UnsupportedOperationException();
+ return "https";
}
public void setServerName(String serverName) {
@@ -455,7 +455,7 @@ public class ProxyHttpServletRequest implements HttpServletRequest {
@Override
public String getServerName() {
- throw new UnsupportedOperationException();
+ return "spring-serverless-web-proxy";
}
public void setServerPort(int serverPort) {
@@ -464,7 +464,7 @@ public class ProxyHttpServletRequest implements HttpServletRequest {
@Override
public int getServerPort() {
- throw new UnsupportedOperationException();
+ return 0;
}
@Override
@@ -582,7 +582,7 @@ public class ProxyHttpServletRequest implements HttpServletRequest {
*/
@Override
public boolean isSecure() {
- throw new UnsupportedOperationException();
+ return false;
}
@Override
@@ -883,7 +883,7 @@ public class ProxyHttpServletRequest implements HttpServletRequest {
@Override
public StringBuffer getRequestURL() {
- throw new UnsupportedOperationException();
+ return new StringBuffer(this.requestURI);
}
public void setServletPath(String servletPath) {
@@ -902,6 +902,9 @@ public class ProxyHttpServletRequest implements HttpServletRequest {
@Override
@Nullable
public HttpSession getSession(boolean create) {
+ if (this.session == null) {
+ this.session = new ProxyHttpSession(this.servletContext);
+ }
return this.session;
}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpSession.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpSession.java
new file mode 100644
index 000000000..0e41f4836
--- /dev/null
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpSession.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023-2023 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
+ *
+ * https://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.cloud.function.serverless.web;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpSession;
+
+/**
+ *
+ * @author Oleg Zhurakousky
+ * @since 4.x
+ *
+ *
+ */
+public class ProxyHttpSession implements HttpSession {
+
+ private final long creationTime;
+
+ private final String sessionId;
+
+ private final ServletContext servletContext;
+
+ private final Map attributes = new HashMap<>();
+
+ public ProxyHttpSession(ServletContext servletContext) {
+ this.creationTime = new Date().getTime();
+ this.sessionId = UUID.randomUUID().toString();
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ public long getCreationTime() {
+ return this.creationTime;
+ }
+
+ @Override
+ public String getId() {
+ return this.sessionId;
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ return 0;
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return this.servletContext;
+ }
+
+ @Override
+ public void setMaxInactiveInterval(int interval) {
+
+ }
+
+ @Override
+ public int getMaxInactiveInterval() {
+ return 0;
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return this.attributes.get(name);
+ }
+
+ @Override
+ public Enumeration getAttributeNames() {
+ return Collections.enumeration(this.attributes.keySet());
+ }
+
+ @Override
+ public void setAttribute(String name, Object value) {
+ this.attributes.put(name, value);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ this.attributes.remove(name);
+ }
+
+ @Override
+ public void invalidate() {
+
+ }
+
+ @Override
+ public boolean isNew() {
+ return false;
+ }
+
+}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java
index 61724b7da..da07ab890 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyMvc.java
@@ -170,6 +170,7 @@ public class ProxyMvc {
ProxyFilterChain(DispatcherServlet servlet) {
List filters = new ArrayList<>();
servlet.getServletContext().getFilterRegistrations().values().forEach(fr -> filters.add(((ProxyFilterRegistration) fr).getFilter()));
+ servlet.getWebApplicationContext().getBeansOfType(Filter.class).values().forEach(f -> filters.add(f));
Assert.notNull(filters, "filters cannot be null");
Assert.noNullElements(filters, "filters cannot contain null values");
this.filters = initFilterList(servlet, filters.toArray(new Filter[] {}));
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java
index 9adab9664..9b0138fb1 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyServletContext.java
@@ -42,6 +42,8 @@ import jakarta.servlet.descriptor.JspConfigDescriptor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.util.ClassUtils;
+
/**
* Empty no-op representation of {@link ServletContext} to satisfy required dependencies to
* successfully proxy incoming web requests to target web application.
@@ -143,7 +145,9 @@ public class ProxyServletContext implements ServletContext {
@Override
public String getServerInfo() {
- return "serverless-web-proxy";
+ return ClassUtils.isPresent("com.amazonaws.serverless.proxy.spring.SpringLambdaContainerHandler", ClassUtils.getDefaultClassLoader())
+ ? "aws-serverless-java-container/6.0"
+ : "serverless-web-proxy";
}
@Override
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java
index 285867bd9..23941a50f 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.cloud.function.test.app.Pet;
import org.springframework.cloud.function.test.app.PetStoreSpringAppConfig;
import org.springframework.http.HttpStatus;
+import org.springframework.security.test.context.support.WithMockUser;
import static org.assertj.core.api.Assertions.assertThat;
@@ -77,6 +78,7 @@ public class RequestResponseTests {
assertThat(pets.get(0)).isInstanceOf(Pet.class);
}
+ @WithMockUser("spring")
@Test
public void validateGetPojo() throws Exception {
HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets/6e3cc370-892f-4efe-a9eb-82926ff8cc5b");
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java
index 5943efdf2..91a59e19c 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java
@@ -17,24 +17,44 @@
package org.springframework.cloud.function.test.app;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.DefaultSecurityFilterChain;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.context.SecurityContextHolderFilter;
+import org.springframework.security.web.csrf.CsrfFilter;
+import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
@Configuration
@Import({ PetsController.class })
+@EnableWebSecurity
public class PetStoreSpringAppConfig {
+
/*
* Create required HandlerMapping, to avoid several default HandlerMapping
* instances being created
@@ -53,6 +73,53 @@ public class PetStoreSpringAppConfig {
return new RequestMappingHandlerAdapter();
}
+ @Bean
+ public BeanPostProcessor post() {
+ return new BeanPostProcessor() {
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (beanName.equals("securityFilterChain")) {
+ DefaultSecurityFilterChain chain = (DefaultSecurityFilterChain) bean;
+ ArrayList filters = new ArrayList<>();
+ chain.getFilters().forEach(f -> {
+ if (!(f instanceof CsrfFilter)) {
+ filters.add(f);
+ }
+ });
+ bean = new DefaultSecurityFilterChain(chain.getRequestMatcher(), filters);
+ }
+ //System.out.println(beanName);
+ return bean;
+ }
+ };
+ }
+
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http
+ .addFilterBefore(new GenericFilterBean() {
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ securityContext.setAuthentication(UsernamePasswordAuthenticationToken.authenticated("user", "password",
+ Collections.singleton(new SimpleGrantedAuthority("USER"))));
+ HttpSession session = ((HttpServletRequest) request).getSession();
+ session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
+ chain.doFilter(request, response);
+ }
+ }, SecurityContextHolderFilter.class)
+ .authorizeHttpRequests((requests) -> requests
+ .requestMatchers("/", "/pets", "/pets/").permitAll()
+ .anyRequest().authenticated()
+ )
+ .logout((logout) -> logout.permitAll());
+
+ return http.build();
+ }
+
@Bean
public Filter filter() {
return new Filter() {