如何配置Actuator Health根据请求返回不同详细程度的响应内容
完全可以实现,不需要改动你现有自定义HealthIndicator的核心逻辑,以下是两种可直接落地的实现方案,按需选择即可。
方案一:基于Actuator原生权限配置(推荐,零业务代码修改)
Spring Boot Actuator原生支持根据访问授权状态返回不同粒度的健康信息,仅需调整配置、按需适配权限规则即可实现需求:
- 添加应用配置(以YAML格式为例,Properties格式可自行转换配置键):
management: endpoint: health: # 仅授权用户可查看健康检查的详情字段 show-details: when-authorized # 所有访问者都可查看聚合接口下的组件列表,列表中仅展示组件状态,不携带详情 show-components: always # 配置查看详情所需的角色,可根据自身权限体系调整 roles: HEALTH_VIEWER
- 如果你使用Spring Security做访问控制,添加如下路径权限规则:
@Bean public SecurityFilterChain healthSecurityRule(HttpSecurity http) throws Exception { http .securityMatcher("/actuator/health/**") .authorizeHttpRequests(auth -> auth // 根聚合健康接口允许所有访问,仅返回全局状态+各组件状态,无详情 .requestMatchers("/actuator/health").permitAll() // 单组件健康接口需要持有HEALTH_VIEWER角色才能访问,访问时返回全量详情 .requestMatchers("/actuator/health/*").hasRole("HEALTH_VIEWER") ) .httpBasic(Customizer.withDefaults()); return http.build(); }
配置生效后的效果:
- 匿名访问
http://localhost:8080/actuator/health时,所有组件(包括你的my-custom-component)只会返回status字段,不会携带details块,完全满足聚合接口精简返回、减少非必要信息暴露的需求 - 持有
HEALTH_VIEWER角色的访问者请求http://localhost:8080/actuator/health/my-custom-component时,会返回包含detail-a、detail-b的全量详情,和你预期的单组件返回结果完全一致 - 你当前编写的
health()方法逻辑不需要做任何修改
方案二:无权限控制的返回过滤(适合不需要鉴权的场景)
如果你不需要做权限控制,要求所有访问者访问聚合接口都看不到敏感详情、访问单组件接口都能看到全量详情,可以通过自定义健康响应映射器实现,直接在聚合视图下过滤不需要返回的字段:
- 先修改配置,保证详情能被正常加载:
management: endpoint: health: show-details: always show-components: always
- 添加自定义响应映射器,聚合视图下移除组件的敏感详情:
import org.springframework.boot.actuate.endpoint.SecurityContext; import org.springframework.boot.actuate.health.*; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import jakarta.servlet.http.HttpServletRequest; import java.util.Map; @Component public class TrimmedHealthResponseMapper extends HealthWebEndpointResponseMapper { public TrimmedHealthResponseMapper(HealthContributorRegistry contributorRegistry, HealthEndpointGroups groups) { super(contributorRegistry, groups); } @Override public WebEndpointResponse<HealthComponent> map(Health health, SecurityContext securityContext) { WebEndpointResponse<HealthComponent> defaultResponse = super.map(health, securityContext); ServletRequestAttributes requestAttrs = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = requestAttrs.getRequest(); // 仅在访问根聚合健康接口时做字段裁剪 if (request.getRequestURI().endsWith("/actuator/health")) { Health aggregateHealth = (Health) defaultResponse.getBody(); if (aggregateHealth != null && aggregateHealth.getComponents() != null) { Map<String, HealthComponent> components = aggregateHealth.getComponents(); components.replaceAll((componentName, component) -> { if (component instanceof Health) { Health componentHealth = (Health) component; // 全量隐藏详情用这行即可 return Health.status(componentHealth.getStatus()).build(); // 只隐藏部分字段(比如删detail-b留detail-a)就替换为下面的逻辑 // Health.Builder builder = Health.status(componentHealth.getStatus()); // componentHealth.getDetails().forEach((key, value) -> { // if (!"detail-b".equals(key)) { // builder.withDetail(key, value); // } // }); // return builder.build(); } return component; }); } } return defaultResponse; } }
提示:Spring Boot 2.x版本使用该代码时,需要把导入包中的
jakarta.servlet替换为javax.servlet。
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

