在过滤器中识别对 Spring Boot Actuator 端点的请求

Identify request to a Spring Boot Actuator endpoint in Filter

如何识别请求是针对 javax.servlet.Filter 中的任何 Spring 个引导执行器端点?

public class MyFilter implements Filter {

  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
    throws IOException, ServletException {

    // Is req to any of Spring Actuator endpoint?

    chain.doFilter(req, res);
  }

我的想法是搜索调用程序 class 或程序包 (org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter),但我不知道具体如何。

这是启动时应用程序日志的一部分。

EndpointHandlerMapping       : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
EndpointHandlerMapping       : Mapped "{[/env || /env.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/archaius || /archaius.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/health || /health.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest,java.security.Principal)
EndpointHandlerMapping       : Mapped "{[/features || /features.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/prometheus || /prometheus.json],methods=[GET]}" onto public java.lang.Object io.micrometer.spring.autoconfigure.export.prometheus.PrometheusScrapeMvcEndpoint.invoke()
EndpointHandlerMapping       : Mapped "{[/loggers || /loggers.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/info || /info.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/env],methods=[POST]}" onto public java.lang.Object org.springframework.cloud.context.environment.EnvironmentManagerMvcEndpoint.value(java.util.Map<java.lang.String, java.lang.String>)
EndpointHandlerMapping       : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
EndpointHandlerMapping       : Mapped "{[/refresh || /refresh.json],methods=[POST]}" onto public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke()

您可以将执行器上下文路径更改为 /management,以便所有请求都到达 http://<host>:<port>/management/<here-come-your-endpoints>

在我们使用的Spring Boot 1.5.3中可以通过指定属性轻松完成:

management.context-path=/management

application.properties文件中

这使得执行器易于跟踪,并允许在网关公开什么和不公开什么时进行灵活配置。

Spring 引导 2:

看来Spring Boot 2的情况每个子版本都不一样。以下代码主要是为了灵感,必须针对特定的 Spring 引导版本进行裁剪:

import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;

public class ActuatorEndpointFilter implements javax.servlet.Filter {

    @Inject
    private WebMvcEndpointHandlerMapping mvcepMapping;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        if (request instanceof HttpServletRequest) {
            HandlerExecutionChain handlerChain = mvcepMapping.getHandler((HttpServletRequest) request);
            if (handlerChain != null) {
                Object handler = handlerChain.getHandler();
                // Following condition is enought in Spring Boot 2.1.1 and 2.1.2.
                if (handler.getClass().getName().startsWith(
                        "org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping")) {
                    // IT IS ACTUATOR ENDPOINT in Spring 2.1.1 and 2.1.2!!!!
                }

                // Following conditions are mandatory in Spring Boot <= 2.1.0.RELEASE
                if (handler instanceof HandlerMethod) { 
                    Object bean = ((HandlerMethod) handler).getBean();
                    if (bean.getClass().getName().startsWith(
                            "org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping")) {
                        // IT IS ACTUATOR ENDPOINT in Spring 2.1.0!!!!
                    }
                }
           }
        }
        chain.doFilter(request, response);
    }
}

我怀疑第一个条件,if (handlerChain != null) 即:如果 WebMvcEndpointHandlerMapping returns 一个处理程序,目前对于任何 Spring Boot 2版本,不过我没有深究。