Spring Boot中如何拦截运行在独立端口的actuator health请求
当你配置actuator使用独立于业务端口的8090端口时,Spring Boot会为管理端口创建独立的Web上下文,和运行在8080的主业务上下文完全隔离。你之前的实现直接调用WebMvcEndpointHandlerMapping.setInterceptors(),会覆盖掉actuator默认注册的所有内置拦截器,其中包含负责将请求路由到对应端点的核心处理逻辑,因此8090端口的请求找不到对应的处理器返回404;而主业务端口的上下文不受管理端口HandlerMapping配置影响,因此8080端口的health接口可以正常响应。
推荐使用专门针对管理上下文的配置方式,不会影响主业务逻辑,也不会破坏actuator原有能力:
步骤1:实现自定义拦截器
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; public class MyActuatorInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 此处编写你的自定义处理逻辑,例如鉴权、请求日志打印、参数校验等 return true; } }
步骤2:编写管理上下文专属配置类
使用@ManagementContextConfiguration注解标识配置类,该注解专门用于管理端口的上下文配置,不会对8080端口的主业务上下文产生影响:
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @ManagementContextConfiguration public class ActuatorInterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 按需配置拦截路径,示例仅拦截health端点,可调整为/actuator/**拦截所有actuator请求 registry.addInterceptor(new MyActuatorInterceptor()) .addPathPatterns("/actuator/health", "/actuator/health/**"); } }
步骤3:注册管理上下文配置
在项目的resources/META-INF/spring目录下创建名为org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports的文件,写入配置类的全限定名(替换为你实际的包路径):
com.yourpackage.config.ActuatorInterceptorConfig
如果你使用Spring Boot 2.6及更早版本,也可以通过在启动类添加@Import(ActuatorInterceptorConfig.class)配合@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)注解实现注册。
如果你不想调整现有代码结构,只需要将原有setInterceptors逻辑从替换拦截器改为追加自定义拦截器即可,不要覆盖actuator的内置拦截器:
@Override public WebMvcEndpointHandlerMapping webMvcEndpointHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) { WebMvcEndpointHandlerMapping mapping = super.webMvcEndpointHandlerMapping(webEndpointsSupplier, servletEndpointsSupplier, controllerEndpointsSupplier, endpointMediaTypes, corsProperties, webEndpointProperties, environment); // 先获取原有拦截器列表 Object[] existingInterceptors = mapping.getInterceptors(); // 追加自定义拦截器到拦截器列表末尾 Object[] newInterceptors = new Object[existingInterceptors.length + 1]; System.arraycopy(existingInterceptors, 0, newInterceptors, 0, existingInterceptors.length); newInterceptors[existingInterceptors.length] = myActuatorInterceptor(); // 重新设置合并后的拦截器列表 mapping.setInterceptors(newInterceptors); return mapping; }
内容的提问来源于stack exchange,提问作者Prasant

