如果要将Spring Security的请求限制在
/springpath/*
你可以绘制地图
Spring Security's FilterChainProxy
.
弹簧靴
在Spring-Boot应用程序中,Spring-Boot的
SecurityFilterConfiguration
automatically registers Spring Security's
FilterChainProxy
与每个网址。您可以通过几个步骤来覆盖它:
SecurityFilterAutoConfiguration
包括以下内容:
@SpringBootApplication(exclude = SecurityFilterAutoConfiguration.class)
接下来,你必须提供你自己的
FilterRegistrationBean
. 这是一份稍微修改过的
安全过滤器自动配置
.
import java.util.Collections;
import java.util.EnumSet;
import java.util.stream.Collectors;
import javax.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
@Configuration
public class SecurityFilterConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
registration.setUrlPatterns(Collections.singleton("/springpath/*"));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name()))
.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
}
}
您可以添加一个简单的测试来验证除非路径以
/springpath/
@SpringBootTest
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
MockMvc mockMvc;
@Test
void firewallWhenSpringPathThenEnabled() {
assertThatExceptionOfType(RequestRejectedException.class)
.isThrownBy(() -> mockMvc.perform(request("INVALID", URI.create("/springpath/bar"))));
assertThatExceptionOfType(RequestRejectedException.class)
.isThrownBy(() -> mockMvc.perform(request("INVALID", URI.create("/springpath/foo/bar"))));
}
@Test
void firewallWhenNotSpringPathThenNotEnabled() {
assertThatNoException()
.isThrownBy(() -> mockMvc.perform(request("INVALID", URI.create("/foo/bar"))));
}
}
你可以在
https://github.com/rwinch/spring-security-sample/tree/so-64824460-firewall-subset-requests
非启动应用程序
对于不使用Spring Boot的应用程序,它不能利用
过滤注册bean
AbstractSecurityWebApplicationInitializer
,但它不允许重写
过滤器链氧
已注册,因为通常不建议使用。这意味着这些应用程序需要创建一个自定义的
WebApplicationInitializer
注册
Filter
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.util.Assert;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.filter.DelegatingFilterProxy;
public class SecurityWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
String filterName = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(filterName);
registerFilter(servletContext, filterName, springSecurityFilterChain);
}
private void registerFilter(ServletContext servletContext, String filterName,
Filter filter) {
FilterRegistration.Dynamic registration = servletContext.addFilter(filterName, filter);
Assert.state(registration != null, () -> "Duplicate Filter registration for '" + filterName
+ "'. Check to ensure the Filter is only configured once.");
EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
registration.addMappingForUrlPatterns(dispatcherTypes, true, "/springpath/*");
}
/**
* Get the {@link DispatcherType} for the springSecurityFilterChain.
* @return
*/
protected EnumSet<DispatcherType> getSecurityDispatcherTypes() {
return EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC);
}
}
这个
ApplicationContext
仍然需要注册。通常可以使用
AbstractAnnotationConfigDispatcherServletInitializer
. 例如:
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { ApplicationConfiguration.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new HiddenHttpMethodFilter() };
}
}
AbstractContextLoaderListenerInitializer
相反。
import org.springframework.web.context.AbstractContextLoaderInitializer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
public class WebApplicationContextInitializer extends AbstractContextLoaderInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(ApplicationConfiguration.class);
return context;
}
}
单元测试使用
MockMvc
FilterRegistration
public class FirewallITests {
private RestTemplate rest;
private int port;
@BeforeEach
void setup() {
this.port = Integer.parseInt(System.getProperty("app.httpPort"));
this.rest = new RestTemplate();
}
@Test
void firewallWhenSpringPathThenEnabled() {
assertThatExceptionOfType(HttpServerErrorException.InternalServerError.class).
isThrownBy(() -> this.rest.getForObject("http://localhost:" + this.port +"/springpath/;/", String.class));
assertThatExceptionOfType(HttpServerErrorException.InternalServerError.class).
isThrownBy(() -> this.rest.getForObject("http://localhost:" + this.port +"/springpath/foo/;/", String.class));
}
@Test
void firewallWhenNotSpringPathThenNotEnabled() {
assertThatNoException().
isThrownBy(() -> this.rest.getForObject("http://localhost:" + this.port +"/bar/;/", String.class));
}
}
https://github.com/rwinch/spring-security-sample/tree/so-64824460-firewall-subset-requests-non-boot