如何在Spring Boot应用中对RestTemplate日志中的用户名、密码信息脱敏
Spring Boot中RestTemplate日志敏感信息脱敏方案
下面是几种实用的方法,用来给RestTemplate日志里的用户名、密码、client_secret这类敏感字段脱敏:
方法一:自定义请求拦截器,在请求链路中脱敏
通过实现ClientHttpRequestInterceptor,在请求发送前替换请求体里的敏感参数,这样日志输出的就是脱敏后的内容。
实现脱敏拦截器
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; public class SensitiveDataInterceptor implements ClientHttpRequestInterceptor { // 匹配表单格式的敏感参数 private static final Pattern SENSITIVE_PARAMS_PATTERN = Pattern.compile( "(password|client_secret|username)=([^&]+)", Pattern.CASE_INSENSITIVE); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // 只处理表单类型的请求 if (body.length > 0 && request.getHeaders().getContentType() != null && request.getHeaders().getContentType().toString().contains("application/x-www-form-urlencoded")) { String bodyStr = new String(body, StandardCharsets.UTF_8); Matcher matcher = SENSITIVE_PARAMS_PATTERN.matcher(bodyStr); // 替换敏感参数值为*** String maskedBody = matcher.replaceAll(matchResult -> matchResult.group(1) + "=***"); body = maskedBody.getBytes(StandardCharsets.UTF_8); } return execution.execute(request, body); } }
注册拦截器到RestTemplate
@Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.getInterceptors().add(new SensitiveDataInterceptor()); return restTemplate; }
如果是JSON格式的请求,需要把正则替换成JSON字段匹配逻辑,比如用Jackson解析后替换敏感字段再序列化。
方法二:Logback自定义转换器,日志层面脱敏
直接在日志输出环节修改消息内容,不影响实际请求,适合只需要日志脱敏的场景。
实现日志脱敏转换器
import ch.qos.logback.classic.pattern.ClassicConverter; import ch.qos.logback.classic.spi.ILoggingEvent; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RestTemplateSensitiveDataConverter extends ClassicConverter { // 匹配RestTemplate日志里的敏感字段格式,比如password=[FooBar..] private static final Pattern SENSITIVE_DATA_PATTERN = Pattern.compile( "(password|client_secret|username)=\\[([^\\]]+)\\]", Pattern.CASE_INSENSITIVE); @Override public String convert(ILoggingEvent event) { String message = event.getMessage(); Matcher matcher = SENSITIVE_DATA_PATTERN.matcher(message); // 替换为*** return matcher.replaceAll(matchResult -> matchResult.group(1) + "=[***]"); } }
配置Logback使用转换器
在logback-spring.xml里添加以下配置:
<configuration> <!-- 注册自定义转换器 --> <conversionRule conversionWord="maskedMessage" converterClass="com.yourpackage.RestTemplateSensitiveDataConverter"/> <!-- 控制台输出用脱敏后的消息 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %maskedMessage%n</pattern> </encoder> </appender> <!-- 给RestTemplate的日志单独配置 --> <logger name="org.springframework.web.client.RestTemplate" level="DEBUG" additivity="false"> <appender-ref ref="CONSOLE"/> </logger> <root level="INFO"> <appender-ref ref="CONSOLE"/> </root> </configuration>
方法三:替换默认日志拦截器,定制日志输出
RestTemplate默认用LoggingRequestInterceptor记录请求日志,我们可以自定义这个拦截器,在日志输出前脱敏。
实现脱敏日志拦截器
import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.LoggingRequestInterceptor; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MaskedLoggingRequestInterceptor extends LoggingRequestInterceptor { private static final Pattern SENSITIVE_PARAMS_PATTERN = Pattern.compile( "(password|client_secret|username)=([^&]+)", Pattern.CASE_INSENSITIVE); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { if (body.length > 0 && logger.isDebugEnabled()) { String bodyStr = new String(body, StandardCharsets.UTF_8); Matcher matcher = SENSITIVE_PARAMS_PATTERN.matcher(bodyStr); String maskedBody = matcher.replaceAll("$1=***"); // 输出脱敏后的日志 logger.debug("Writing [{}] as \"{}\"", maskedBody, request.getHeaders().getContentType()); } return execution.execute(request, body); } }
注册自定义拦截器
@Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); // 移除默认的日志拦截器 restTemplate.getInterceptors().removeIf(interceptor -> interceptor instanceof LoggingRequestInterceptor); // 添加自定义脱敏日志拦截器 restTemplate.getInterceptors().add(new MaskedLoggingRequestInterceptor()); return restTemplate; }
各方法优缺点对比
- 请求拦截器:从请求源头处理,不仅日志脱敏,还能避免其他环节泄露敏感数据,但需要适配不同的请求格式(表单、JSON等)。
- Logback转换器:只作用于日志层,不影响业务逻辑,配置简单,适合仅需日志脱敏的场景。
- 自定义日志拦截器:贴合RestTemplate原有日志逻辑,输出格式和原日志一致,无需修改全局日志配置。
内容的提问来源于stack exchange,提问作者user352290
相关产品推荐
相关产品推荐

