我有一个Spring Boot(1.5.6)应用程序正在使用Spring Security的“pre-authenticated”身份验证方案(SiteMinder)。
我需要匿名公开执行器“运行状况”端点,这意味着对该端点的请求不会通过SiteMinder,因此,HTTP请求标头中不会出现SM_USER标头。
我面临的问题是,无论我如何尝试配置“运行状况”端点,框架都会抛出org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException
,因为当请求未通过SiteMinder时,预期的标头(“SM_USER”)不存在。
这是我最初的安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().antMatchers("/cars/**", "/dealers/**")
.hasAnyRole("CLIENT", "ADMIN")
.and()
.authorizeRequests().antMatchers("/health")
.permitAll()
.and()
.authorizeRequests().anyRequest().denyAll()
.and()
.addFilter(requestHeaderAuthenticationFilter())
.csrf().disable();
}
@Bean
public Filter requestHeaderAuthenticationFilter() throws Exception {
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager());
return filter;
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(preAuthProvider());
}
@Bean
public AuthenticationProvider preAuthProvider() {
PreAuthenticatedAuthenticationProvider authManager = new PreAuthenticatedAuthenticationProvider();
authManager.setPreAuthenticatedUserDetailsService(preAuthUserDetailsService());
return authManager;
}
@Bean
public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> preAuthUserDetailsService() {
return new UserDetailsByNameServiceWrapper<>(inMemoryUserDetails());
}
@Bean
public UserDetailsService inMemoryUserDetails() {
return new InMemoryUserDetailsManager(getUserSource().getUsers());
}
@Bean
public UserHolder getUserHolder() {
return new UserHolderSpringSecurityImple();
}
@Bean
@ConfigurationProperties
public UserSource getUserSource() {
return new UserSource();
}
我试图以几种不同的方式排除/ health端点无济于事。
我试过的事情:
为匿名访问而不是permitAll配置健康端点:
http
.authorizeRequests().antMatchers("/health")
.anonymous()
配置WebSecurity以忽略运行状况终结点:
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/health");
}
关闭所有执行器端点的安全性(不知道,但我抓住了吸管):
management.security.enabled=false
查看日志,问题似乎是RequestHeaderAuthenticationFilter被注册为顶级过滤器而不是现有securityFilterChain中的过滤器:
.s.DelegatingFilterProxyRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'webRequestLoggingFilter' to: [/*]
o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestHeaderAuthenticationFilter' to: [/*]
基于我的理解,因为RequestHeaderAuthenticationFilter
扩展了AbstractPreAuthenticatedProcessingFilter
,所以框架知道在链中插入过滤器的位置,这就是为什么我没有修改addFilterBefore或addFilterAfter变体。也许我应该?有人知道明确插入过滤器的正确位置吗? (我认为在Spring Security的早期版本中删除了显式指定过滤器顺序的需要)
我知道我可以配置RequestHeaderAuthenticationFilter
,这样如果标头不存在它就不会抛出异常,但是如果可能的话我想保持它。
我发现这个SO帖子似乎与我的问题相似,但遗憾的是那里也没有答案。 spring-boot-security-preauthentication-with-permitted-resources-still-authenti
任何帮助将不胜感激!谢谢!
问题确实是RequestHeaderAuthenticationFilter既被注册为顶级过滤器(不需要的),也被注册在Spring Security FilterChain(期望的)中。
“双重注册”的原因是因为Spring Boot会自动向Servlet容器注册任何Filter
Bean。
为了防止“自动注册”,我只需要像这样定义一个FilterRegistrationBean
:
@Bean
public FilterRegistrationBean registration(RequestHeaderAuthenticationFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
一个替代/更简单的解决方案:只是不要将RequestHeaderAuthenticationFilter
类标记为@Bean
,这很好,因为该特定滤波器不需要任何DI。通过不使用@Bean
标记过滤器,Boot不会尝试自动注册它,这样就无需定义FilterRegistrationBean。