Spring Boot Security PreAuthenticated 场景,匿名访问
Posted
技术标签:
【中文标题】Spring Boot Security PreAuthenticated 场景,匿名访问【英文标题】:Spring Boot Security PreAuthenticated Scenario with Anonymous access 【发布时间】:2018-08-08 18:06:03 【问题描述】:我有一个使用 Spring Security 的“pre-authenticated”身份验证方案 (SiteMinder) 的 Spring Boot (1.5.6) 应用程序。
我需要匿名公开执行器“健康”端点,这意味着对该端点的请求将不通过 SiteMinder,因此 SM_USER 标头不会出现在 HTTP 请求中标题。
我面临的问题是,无论我如何尝试配置“健康”端点,框架都会抛出 org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException
,因为当请求不通过时,预期的标头(“SM_USER”)不存在通过 SiteMinder。
这是我最初的安全配置:
@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 被注册为 top level 过滤器,而不是现有 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 或 addFilter 之后变体。也许我应该是?有人知道明确插入过滤器的正确位置吗? (我认为在 Spring Security 的早期版本中删除了显式指定过滤器顺序的需要)
我知道我可以配置RequestHeaderAuthenticationFilter
,这样如果标头不存在,它就不会引发异常,但我想尽可能保持这种状态。
我发现这个 SO 帖子似乎与我的问题相似,但不幸的是那里也没有答案。 spring-boot-security-preauthentication-with-permitted-resources-still-authenti
任何帮助将不胜感激! 谢谢!
【问题讨论】:
Filter invoke twice when register as Spring bean的可能重复 【参考方案1】:问题确实是 RequestHeaderAuthenticationFilter 被注册为 both 作为***过滤器(不需要)以及在 Spring Security FilterChain 中(需要)。
“双重注册”的原因是因为 Spring Boot 会自动向 Servlet Container 注册任何 Filter
Bean。
为了防止“自动注册”,我只需要像这样定义一个FilterRegistrationBean
:
@Bean
public FilterRegistrationBean registration(RequestHeaderAuthenticationFilter filter)
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
文档: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-disable-registration-of-a-servlet-or-filter
另一种/更简单的解决方案:
只是不要将RequestHeaderAuthenticationFilter
类标记为@Bean
,这很好,因为该特定过滤器不需要任何类型的DI。通过不使用@Bean
标记过滤器,Boot 不会尝试自动注册它,从而无需定义 FilterRegistrationBean。
【讨论】:
【参考方案2】:所提供的答案很可能并不完整,因为 Sean Casey 进行了如此多的试验和错误更改,以至于忘记了实际解决问题的配置。我发布了我认为具有正确配置的发现:
如果您错误地将RequestHeaderAuthenticationFilter
注册为@Bean
,如原始答案所述,则将其删除。只需将其创建为普通实例并将其添加为过滤器即可正确注册它:
@Override
protected void configure(HttpSecurity http) throws Exception
RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
// configure your filter
http
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilter(filter)
.csrf().disable();
Sean Casey 尝试但最初失败的神奇配置(由于 auth 过滤器的双重注册)是 WebSecurity
配置:
@Override
public void configure(WebSecurity web) throws Exception
web.ignoring().antMatchers("/health");
请注意,在这种情况下添加 HttpSecurity
antMatchers
没有任何作用,似乎 WebSecurity
是优先处理并控制所发生的事情。
额外:根据 Spring Security 文档,WebSecurity.ignore
方法应该用于静态资源,而不是用于应该映射以允许所有用户的动态资源。但是在这种情况下,映射似乎被 PreAuthentication 过滤器覆盖,这会强制使用更激进的ignore
方案。
【讨论】:
以上是关于Spring Boot Security PreAuthenticated 场景,匿名访问的主要内容,如果未能解决你的问题,请参考以下文章
Understand Spring Security Architecture and implement Spring Boot Security
如何使用 Spring-Boot 播种 Spring-Security