1

我有一个 Spring Boot (1.5.6) 应用程序,它使用来自 Spring Security 的“预身份验证”身份验证方案 (SiteMinder)。

我需要匿名公开执行器“健康”端点,这意味着对该端点的请求不会通过 SiteMinder,因此 SM_USER 标头不会出现在 HTTP 请求标头中。

我面临的问题是,无论我如何尝试配置“健康”端点,框架都会抛出一个错误,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: [/*]

根据我的理解,因为RequestHeaderAuthenticationFilterextends AbstractPreAuthenticatedProcessingFilter,框架知道在链中插入过滤器的位置,这就是为什么我不修改 addFilter Before或 addFilter After变体的原因。也许我应该是?有人知道明确插入过滤器的正确位置吗?(我认为在 Spring Security 的早期版本中删除了显式指定过滤器顺序的需要)

我知道我可以配置它,RequestHeaderAuthenticationFilter这样如果标题不存在,它就不会抛出异常,但如果可能的话,我想保持这种状态。

我发现这个 SO 帖子似乎与我的问题相似,但不幸的是那里也没有答案。 spring-boot-security-preauthentication-with-permitted-resources-still-authenti

任何帮助将不胜感激!谢谢!

4

2 回答 2

3

问题确实是 RequestHeaderAuthenticationFilter 被注册顶级过滤器(不需要)Spring Security FilterChain(需要)。

“双重注册”的原因是因为 Spring Boot 会Filter自动向 Servlet Container 注册任何 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 的需要。

于 2018-02-28T20:05:35.397 回答
1

提供的答案很可能并不完整,因为 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方案。

于 2020-12-14T16:26:46.670 回答