GH-1034 Add implementation of ProxyHttpSession, fix Spring Security interaction
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, Object> 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<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -170,6 +170,7 @@ public class ProxyMvc {
|
||||
ProxyFilterChain(DispatcherServlet servlet) {
|
||||
List<Filter> 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[] {}));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<Filter> 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() {
|
||||
|
||||
Reference in New Issue
Block a user