Spring Boot Security PreAuthenticated Scenario with Anonymous access

a 夏天 提交于 2020-01-25 08:06:46

问题


I have a Spring Boot (1.5.6) application that is using the "pre-authenticated" authentication scenario (SiteMinder) from Spring Security.

I have a need to expose the actuator "health" endpoint anonymously meaning the requests to that endpoint will not go through SiteMinder and as a result, the SM_USER header will not be present in the HTTP Request Header.

The problem I'm facing is that no matter how I try to configure the "health" endpoint, the framework is throwing an org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException because the expected header ("SM_USER") is not present when the request does not go through SiteMinder.

This was my original security config:

@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();
    }

I've tried to exclude the /health endpoint a couple different ways to no avail.

Things I've tried:

Configure health endpoint for anonymous access rather than permitAll:

http
   .authorizeRequests().antMatchers("/health")
   .anonymous()

Configure WebSecurity to ignore the health endpoint:

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/health");
}

Turn off security for all actuator endpoints (not idea but I was grasping for straws):

management.security.enabled=false

Looking at the logs, the problem seems to be that the RequestHeaderAuthenticationFilter is getting registered as a top level filter rather than a filter in the existing 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: [/*]

Based on my understanding, because the RequestHeaderAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter, the framework knows where to insert the filter within the chain which is why I'm not tinkering with the addFilterBefore or addFilterAfter variants. Maybe I should be? Does anybody know the correct place to insert the filter explicitly? (I thought the need for explicitly specifying filter order was removed in prior versions of Spring Security)

I know I can configure the RequestHeaderAuthenticationFilter so that it doesn't throw an exception if the header is not present but I'd like to keep that on if at all possible.

I found this SO post that seems to be similar to my problem but unfortunately there's no answer there either. spring-boot-security-preauthentication-with-permitted-resources-still-authenti

Any help would be greatly appreciated! Thanks!


回答1:


The problem was indeed the fact that the RequestHeaderAuthenticationFilter was being registered both as a top level filter (unwanted) and also within the Spring Security FilterChain (desired).

The reason for the "double registration" is because Spring Boot will register any Filter Beans with the Servlet Container automatically.

In order to prevent the "auto-registration" I just had to define a FilterRegistrationBean like so:

@Bean
public FilterRegistrationBean registration(RequestHeaderAuthenticationFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.setEnabled(false);
    return registration;
}

Docs: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-disable-registration-of-a-servlet-or-filter

An alternate/simpler solution: Just don't mark the RequestHeaderAuthenticationFilter class as an @Bean which is fine because there's no kind of DI needed for that particular filter. By not marking the filter with @Bean, Boot won't try to auto register it which removes the need to define the FilterRegistrationBean.



来源:https://stackoverflow.com/questions/49036697/spring-boot-security-preauthenticated-scenario-with-anonymous-access

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!