spring 具有 Keycloak 安全性的引导执行器端点

spring boot actuator endpoints with Keycloak security

我们有一个带有执行器端点的 spring 引导项目 (2.3.0.RELEASE),我们正在使用 KeycloakWebSecurityConfigurerAdapter 将 keycloak 引入到项目中,我如何防止执行器端点被 keycloak 过滤器链保护.

我们希望通过基本身份验证保护“/actuator/**”端点。

目前我们有一个带有@Order(1) 的自定义WebSecurityConfigurerAdapter,我们将基本身份验证应用于“/actuator/**”,然后我们有@Order(2) antotated KeycloakWebSecurityConfigurerAdapter

因此注册了 2 个过滤器链,当我调用执行器端点时,第二个过滤器链因未授权 401 而失败

是否可以阻止处理第二个过滤器链上的“/actuator/**”资源路径?

第一个执行器安全配置。

@Configuration
@Order(1)
public class ActuatorWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final String username;
    private final String password;
    private final PasswordEncoder encoder;

    public ActuatorWebSecurityConfig(
            @Value("${spring.security.user.name}") String username,
            @Value("${spring.security.user.password}") String password,
            Optional<PasswordEncoder> encoder) {
        this.username = username;
        this.password = password;
        this.encoder = encoder.orElseGet(PasswordEncoderFactories::createDelegatingPasswordEncoder);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser(username)
                .password(encoder.encode(password))
                .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .antMatcher("/actuator/**")
                .authorizeRequests(authorize -> authorize.anyRequest().authenticated())
                .httpBasic(Customizer.withDefaults());
    }
}

第二个 keycloak 安全配置

@Order(2)
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    private final String swaggerUrl;
    private final CorsFilter corsFilter;
    private final CustomSecurityConfig customSecurityConfig;

    @Autowired
    public SecurityConfig(
            @Value("${springdoc.swagger-ui.url:#{null}}") String swaggerUrl,
            CorsFilter corsFilter,
            CustomSecurityConfig customSecurityConfig) {
        this.swaggerUrl = swaggerUrl;
        this.corsFilter = corsFilter;
        this.customSecurityConfig = customSecurityConfig;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakProvider = keycloakAuthenticationProvider();
        keycloakProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().disable()
            .requestMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/actuator/**")));
            .headers().frameOptions().disable()
        .and()
           .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
           .antMatchers("/public/**", "/resources/**", "/resources/public/**").permitAll()
           .antMatchers(OPTIONS, "/**").permitAll();
        .authorizeRequests()
           .antMatchers("/**")
           .authenticated();
    }
}

我已经尝试使用 keycloak 配置

.antMatchers("/actuator/**").permitAll();

并与

http.requestMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/actuator/**")));

但没有任何效果我收到执行器未经授权的 401

已注册的过滤器链:

2022-01-18 17:38:44,688 INFO org.springframework.security.web.DefaultSecurityFilterChain [main] Creating filter chain: Ant [pattern='/actuator/**'], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@25c6a9de, org.springframework.security.web.context.SecurityContextPersistenceFilter@56f3f9da, org.springframework.security.web.header.HeaderWriterFilter@33dcbdc2, org.springframework.security.web.csrf.CsrfFilter@522fdf0c, org.springframework.security.web.authentication.logout.LogoutFilter@365ad794, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@23df16cf, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@227cba85, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@b38dc7d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@142422a4, org.springframework.security.web.session.SessionManagementFilter@2f0b7b6d, org.springframework.security.web.access.ExceptionTranslationFilter@74bca236, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@30587737]
2022-01-18 17:38:44,691 INFO org.springframework.security.web.DefaultSecurityFilterChain [main] Creating filter chain: NegatedRequestMatcher [requestMatcher=Ant [pattern='/actuator/**']], [com.betex.auth.filters.CorsFilter@20a9f5fb, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@10e28d97, org.springframework.security.web.context.SecurityContextPersistenceFilter@c6b08a5, org.springframework.security.web.header.HeaderWriterFilter@5f05cd7e, org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter@2a54c92e, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter@55b62db8, org.springframework.security.web.authentication.logout.LogoutFilter@274f51ad, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@54980154, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@25874884, org.keycloak.adapters.springsecurity.filter.KeycloakSecurityContextRequestFilter@8cb7185, org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticatedActionsFilter@4dac40b, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@37d43b9b, org.springframework.security.web.session.SessionManagementFilter@11e8e183, org.springframework.security.web.access.ExceptionTranslationFilter@56f1db5f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@78543f0d]

当您扩展 KeycloakWebSecurityConfigurerAdapter 时,适配器会注册一个 KeycloakAuthenticationProcessingFilter 类型的 Bean。这个filter是在SpringSecurity的SecurityFilterChain注册的,因为是Bean,所以在原链中SpringBoot也会自动注册,所以即使SpringSecurity不应用它,稍后将在原始过滤器链中应用它。

尝试禁止此过滤器被 Spring Boot 注册,像这样:

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

此外,如果您使用的是 OAuth 2,您可以考虑使用 spring-security-oauth2-resource-server 并简化资源服务器的配置。看看 documentation。这样您就不需要扩展自定义适配器,只需依赖 Spring Security.

的开箱即用配置