Spring 引导执行器端点安全不适用于自定义 Spring 安全配置

Spring Boot Actuator Endpoints security doesn't work with custom Spring Security Configuration

这是我的 Spring Boot 1.5.1 Actuator application.properties:

#Spring Boot Actuator
management.contextPath: /actuator
management.security.roles=R_0

这是我的 WebSecurityConfig:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Value("${logout.success.url}")
    private String logoutSuccessUrl;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);

        http
            .csrf().ignoringAntMatchers("/v1.0/**", "/logout")
        .and()
            .authorizeRequests()

            .antMatchers("/oauth/authorize").authenticated()
            //Anyone can access the urls
            .antMatchers("/signin/**").permitAll()
            .antMatchers("/v1.0/**").permitAll()
            .antMatchers("/auth/**").permitAll()
            .antMatchers("/actuator/health").permitAll()
            .antMatchers("/actuator/**").hasAuthority("R_0")
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .failureUrl("/login?error=true")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
            .and()
                .logout()
                    .logoutUrl("/logout")
                    .logoutSuccessUrl(logoutSuccessUrl)
                    .permitAll();
        // @formatter:on
    }

    /**
     * Configures the authentication manager bean which processes authentication requests.
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}

现在我可以使用具有 R_0 权限的正确用户成功登录我的应用程序,但是当我尝试访问时

http://localhost:8080/api/actuator/beans

我收到以下错误:

There was an unexpected error (type=Forbidden, status=403).
Access is denied. User must have one of the these roles: R_0

如何正确配置 Spring Boot Actuator 以了解正确的 Authentication ?

现在为了让它正常工作,我必须执行以下操作:

management.security.enabled=false

.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority("R_0")

是否有机会以正确的方式配置执行器?

已更新

我正在使用 UserDetailsService.UserDetails.Authorities

    public Collection<? extends GrantedAuthority> getAuthorities() {
        String[] authorities = permissions.stream().map(p -> {
            return p.getName();
        }).toArray(String[]::new);
        return AuthorityUtils.createAuthorityList(authorities);
    }

据此link:

http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-monitoring.html

By default all sensitive HTTP endpoints are secured such that only users that have an ACTUATOR role may access them. Security is enforced using the standard HttpServletRequest.isUserInRole method.

Use the management.security.roles property if you want something different to ACTUATOR.

所以我认为你所要做的就是在 application.properties 中设置以下 属性。

management.security.roles

例如:

management.security.roles=R_0

要获得 spring 引导执行器端点的授权,您需要具有 ACTUATOR 角色。 参考这个例子Accessing Restricted Actuator Endpoints with Spring Security

您必须为您的 management.security.roles 使用前缀 ROLE_ 例如 management.security.roles=ROLE_SOMENAME 才能解决此问题

我是从 Reactive Spring Boot 2.x 应用程序来解决这个问题的,并通过将 WebSecurityConfig.securityWebFilterChain 和 SecurityContextRepository.load 更新为包括 /actuator/** 如下:

public class WebSecurityConfig {
  private AuthenticationManager authenticationManager;

  private SecurityContextRepository securityContextRepository;

  @Autowired
  public WebSecurityConfig(AuthenticationManager authenticationManager, SecurityContextRepository securityContextRepository) {
    this.authenticationManager = authenticationManager;
    this.securityContextRepository = securityContextRepository;
  }

  @Bean
  public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    return http
      .exceptionHandling()
      .authenticationEntryPoint((swe, e) -> Mono.fromRunnable(() -> {
        swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      })).accessDeniedHandler((swe, e) -> Mono.fromRunnable(() -> {
        swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
      })).and()
      .csrf().disable()
      .formLogin().disable()
      .httpBasic().disable()
      .authenticationManager(authenticationManager)
      .securityContextRepository(securityContextRepository)
      .authorizeExchange()
      .pathMatchers("/actuator/**").permitAll()
      .anyExchange().authenticated()
      .and().build();
  }

以及更新

@Slf4j
@Component
public class SecurityContextRepository implements ServerSecurityContextRepository {

  private AuthenticationManager authenticationManager;

  public SecurityContextRepository(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
  }

  @Override
  public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
    return Mono.error(new UnsupportedOperationException("Not supported"));
  }

  @Override
  public Mono<SecurityContext> load(ServerWebExchange swe) {
    ServerHttpRequest request = swe.getRequest();

    if (request.getPath().value().startsWith("/actuator") ) {
      return Mono.empty();
    }
    // other authentication logic here
  }
1
1.You can add the following properties
endpoints.health.enabled=true
endpoints.loggers.enabled=true
endpoints.metrics.enabled=true

endpoints.env.enabled=false
endpoints.configprops.enabled=false
endpoints.autoconfig.enabled=false
endpoints.info.enabled=false

management.context-path=/management

2)
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ApplicationContextAware {

    private static final String ACTUATOR = "ACTUATOR";
    private static final String ROLE1 = "USER";

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().authorizeRequests().antMatchers("/controllerpath/**")
                .hasAnyRole(ROLE1).antMatchers("/loggers/**","/metrics/**","/health/**").hasAnyRole(ACTUATOR).and()

                .csrf().disable().headers().frameOptions().disable();

    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("username-of-app").password("encryptedpassword")
                .roles(ROLE1).and().withUser("username-of-management-for-path")
                .password("encrypted password").roles(ACTUATOR);

    }
}
  1. 您可以添加以下属性
    endpoints.health.enabled=true
    endpoints.loggers.enabled=true
    endpoints.metrics.enabled=true
    
    endpoints.env.enabled=false
    endpoints.configprops.enabled=false
    endpoints.autoconfig.enabled=false
    endpoints.info.enabled=false
    
    management.context-path=/management
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ApplicationContextAware {
    
        private static final String ACTUATOR = "ACTUATOR";
        private static final String ROLE1 = "USER";
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.httpBasic().and().authorizeRequests().antMatchers("/controllerpath/**")
                    .hasAnyRole(ROLE1).antMatchers("/loggers/**","/metrics/**","/health/**").hasAnyRole(ACTUATOR).and()
    
                    .csrf().disable().headers().frameOptions().disable();
    
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
            auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                    .withUser("username-of-app").password("encryptedpassword")
                    .roles(ROLE1).and().withUser("username-of-management-for-path")
                    .password("encrypted password").roles(ACTUATOR);
    
        }
    }