【问题标题】:Spring Boot Actuator Endpoints security doesn't work with custom Spring Security ConfigurationSpring Boot Actuator Endpoints 安全性不适用于自定义 Spring Security 配置
【发布时间】:2017-06-27 19:24:03
【问题描述】:

这是我的 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);
    }

【问题讨论】:

  • 谢谢,但我可以通过 .antMatchers("/actuator/**").hasAuthority("R_0") 来管理这个问题。问题是我无法使用默认的 Spring Boot Actuator 安全集成,而无需提供像这样的自定义配置。
  • 我遇到了同样的问题。请注意,通过在 AuthenticationManagerBuilder 上设置 UserDetailsService 会以某种方式覆盖默认安全配置,这意味着需要像通过 HttpSecurity 类所做的那样显式 URI 访问配置。您是否设法使其按您最初的预期工作?
  • 是的,您必须为您的management.security.roles 使用前缀ROLE_,例如management.security.roles=ROLE_SOMENAME
  • 由于我使用的是 Spring Boot 1.3.5,因此我的问题与此属性的复数与单数格式有关。对于我的版本,您只能将一个角色定义为,例如management.security.role=ACTUATOR,在 1.4.x 版本中已更改为复数等效项。此外,我为AuthenticationManagerBuilder 类的UserDetails 检索组合了两个来源,一个是我的服务用户的UserDetailsService 的实现,第二个是执行器和管理员用户的InMemoryUserDetailsManagerConfigurer 的实例(你可以逃脱也只有一个)。

标签: java spring spring-boot spring-security spring-boot-actuator


【解决方案1】:
  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);
    
        }
    }

【讨论】:

    【解决方案2】:
    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);
    
        }
    }
    

    【讨论】:

      【解决方案3】:

      我来自 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
        }
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 我认为在 Spring Boot 1.5.1 中 management.security.roles 属性的值不应再以 ROLE_ 为前缀。我发现很多关于这个主题的困惑。我相信您的问题也可以通过修改以下HttpSecurity设置.antMatchers("/actuator/**").hasAuthority("ROLE_R_0")来解决。
        • antMatcher 不能解决问题。例如,执行器/健康端点为授权用户和非授权用户返回不同的数据。基于提供的antMatcher配置无法实现
        • 您所说的行为记录在docs.spring.io/spring-boot/docs/current/reference/html/… 的第 48.7 节中。您最初的问题是关于 /actuator/beans 呼叫的 403 状态代码。我相信可以通过删除antMatchers("/actuator...") 设置并确保UserDetailsService 返回包含GrantedAuthorityUserDetails 对象来修复它,在您的情况下设置为ROLE_R_0。或者,您可以将其与 InMemoryUserDetailsManagerConfigurer 结合使用,正如我在对主要问题的评论中提到的那样。
        • 它不起作用,因为 Spring Boot Actuator 不理解没有 _ROLE 前缀的权限/角色。 antMatchers 在这里也没有帮助,因为为了使用它,我必须禁用 management.security。禁用此属性后,我将失去上面描述的执行器功能,因此您的解决方案对我完全没有任何意义。
        • 更多详情请参考以下github问题github.com/spring-projects/spring-boot/issues/8255
        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          根据此链接:

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

          默认情况下,所有敏感的 HTTP 端点都是安全的,因此只有 具有 ACTUATOR 角色的用户可以访问它们。强制执行安全性 使用标准的 HttpServletRequest.isUserInRole 方法。

          如果需要,请使用 management.security.roles 属性 不同于 ACTUATOR。

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

          management.security.roles
          

          例如:

          management.security.roles=R_0
          

          【讨论】:

          • 谢谢,但请看看我的问题。我已经提供了 management.security.roles=R_0 问题是 Spring Boot Actuator 出于某种原因不了解如何使用当前的 Spring Security。
          猜你喜欢
          • 2016-05-03
          • 2016-11-25
          • 2016-05-02
          • 2019-02-05
          • 1970-01-01
          • 2016-09-08
          • 2020-10-02
          • 2014-07-16
          相关资源
          最近更新 更多