如何实现Jetty请求日志中password等敏感信息的脱敏处理
方案1:自定义Jetty RequestLog实现(最推荐,从根源解决问题)
Jetty的默认请求日志是在服务连接器层面生效的,优先级远高于Servlet Filter,所有进入Jetty的请求(包括异常、未进入Servlet链的无效请求)都会被它记录,你不需要修改Jetty官方Jar包,直接实现自定义RequestLog替换默认实现即可,既不会产生重复日志,也不会丢失任何请求记录:
- 第一步:实现Jetty的
RequestLog接口,在日志输出前完成敏感字段脱敏,代码示例:
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SensitiveDesensitizeRequestLog extends AbstractLifeCycle implements RequestLog { private static final Logger log = LoggerFactory.getLogger(SensitiveDesensitizeRequestLog.class); // 匹配所有敏感参数的正则,可自行扩展 private static final String SENSITIVE_PATTERN = "(password|secret|token)=[^&]+"; @Override public void log(Request request, Response response) { String queryString = request.getQueryString(); // 脱敏处理 if (queryString != null) { queryString = queryString.replaceAll(SENSITIVE_PATTERN, "$1=redacted"); } // 拼接完整日志内容,格式可和Jetty默认日志保持一致 String logContent = String.format("%s - - \"%s %s%s %s\" %d %d", request.getRemoteAddr(), request.getMethod(), request.getRequestURI(), queryString == null ? "" : "?" + queryString, request.getProtocol(), response.getStatus(), response.getContentLength() ); log.info(logContent); } }
- 第二步:将自定义RequestLog配置到Jetty服务器中,如果是Spring Boot项目,直接添加如下配置类即可:
import org.eclipse.jetty.server.Server; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JettyConfig { @Bean public JettyServerCustomizer desensitizeLogCustomizer() { return server -> { // 关闭Jetty默认RequestLog server.setRequestLog(null); // 注册自定义脱敏RequestLog server.setRequestLog(new SensitiveDesensitizeRequestLog()); }; } }
配置完成后直接删掉你原来的自定义Filter即可,所有请求日志都会自动脱敏,不会出现重复日志,也不会丢失无效请求记录。
方案2:保留现有Filter,解决重复日志和丢日志问题
如果不想改动现有Filter代码,可按如下步骤调整:
- 关闭Jetty默认的RequestLog,避免重复日志
- 调整现有Filter逻辑,补全异常捕获和正则缺陷,确保所有请求(包括异常请求)都能被记录,不会丢日志:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) // 调整为最高优先级,确保所有请求都能进入该Filter public class RequestLogFilter implements Filter { private final static Logger log = LogManager.getLogger(RequestLogFilter.class); private static final String SENSITIVE_PATTERN = "(password|secret|token)=[^&]+"; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) servletRequest; String uriPath = req.getRequestURI(); String queryString = req.getQueryString(); // 修复原正则无法匹配末尾参数的问题 if (queryString != null) { queryString = queryString.replaceAll(SENSITIVE_PATTERN, "$1=redacted"); } String method = req.getMethod(); String ip = req.getRemoteAddr(); String protocol = req.getProtocol(); String logString = ip + " - - \"" + method + " " + uriPath + (queryString == null ? "" : "?" + queryString) + " " + protocol + "\""; try { filterChain.doFilter(servletRequest, servletResponse); } finally { // 放到finally块中,无论后续请求是否抛出异常都能打印日志,避免丢失无效请求记录 log.info(logString); } } @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() { log.warn("Destructing RequestLogFilter"); } }
内容的提问来源于stack exchange,提问作者Durhode Petifort
相关产品推荐
相关产品推荐

