RestTemplate日志脱敏与Java反射提取完整类名的技术咨询
Hey there! Let's break down your two questions and work through better solutions for each one.
1. Hiding Username/Password in RestTemplate Logs
When using RestTemplate to handle login requests and subsequent calls with a webToken, masking sensitive credentials in logs is crucial for security. Here are two reliable approaches:
Option 1: Custom ClientHttpRequestInterceptor
Create an interceptor that detects login requests, masks the sensitive fields in the request body before logging, then proceeds with the request:
import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.charset.StandardCharsets; public class SensitiveDataMaskingInterceptor implements ClientHttpRequestInterceptor { private static final Logger logger = LoggerFactory.getLogger(SensitiveDataMaskingInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { String requestUri = request.getURI().toString(); // Target only login requests if (requestUri.contains("/login")) { String originalBody = new String(body, StandardCharsets.UTF_8); // Mask username and password (adjust regex if your request format differs) String maskedBody = originalBody.replaceAll("\"username\":\"[^\"]*\"", "\"username\":\"***\"") .replaceAll("\"password\":\"[^\"]*\"", "\"password\":\"***\""); logger.info("Sending login request to {} with masked body: {}", requestUri, maskedBody); return execution.execute(request, maskedBody.getBytes(StandardCharsets.UTF_8)); } else { // Log non-login requests normally logger.info("Sending request to {} with body: {}", requestUri, new String(body, StandardCharsets.UTF_8)); return execution.execute(request, body); } } }
Add this interceptor to your RestTemplate:
RestTemplate restTemplate = new RestTemplate(); restTemplate.getInterceptors().add(new SensitiveDataMaskingInterceptor());
Option 2: Logback/SLF4J Custom Converter (Spring Boot)
If you're using Logback, you can create a custom converter to filter sensitive data directly in log outputs. Add this to your logback.xml:
<conversionRule conversionWord="maskedBody" converterClass="com.yourpackage.SensitiveDataConverter"/>
Then implement the converter:
import ch.qos.logback.classic.pattern.ClassicConverter; import ch.qos.logback.classic.spi.ILoggingEvent; public class SensitiveDataConverter extends ClassicConverter { @Override public String convert(ILoggingEvent event) { String message = event.getMessage(); if (message.contains("/login")) { return message.replaceAll("\"username\":\"[^\"]*\"", "\"username\":\"***\"") .replaceAll("\"password\":\"[^\"]*\"", "\"password\":\"***\""); } return message; } }
Update your log pattern to use %maskedBody instead of the raw message.
2. Better Reflection Type Check & Full Class Name Extraction
Looking at your TypeCheck code, the core issue is that you're passing a Class object to a method expecting an Object, then using instanceof incorrectly (you're checking if the Class instance is an Animal, which it never will be). Here's how to fix and optimize this:
Fix 1: Correct Method Parameter Type
Since you're passing animal.getClass() (a Class object), change the method parameter to Class<?> for clarity:
package reflection; public class TypeCheck { public static void main(String[] args) { Object animal = new Animal("Elephant"); classType(animal.getClass()); // Now works correctly // Output - Matched reflection.Animal } private static void classType(Class<?> type) { // Check if the type is Animal or a subclass if (Animal.class.isAssignableFrom(type)) { // getName() returns the full qualified class name (including package) System.out.println("Matched " + type.getName()); } else { System.out.println("Unmatched " + type.getName()); } } } class Animal { String name; public Animal(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
type.getName() will give you exactly the full class name you need (reflection.Animal), and isAssignableFrom() is the proper way to check class hierarchy in reflection.
Fix 2: Handle Both Instance and Class Inputs
If you need the method to accept either an Animal instance or its Class object, adjust the method to detect the input type first:
private static void classType(Object input) { Class<?> clazz; if (input instanceof Class) { clazz = (Class<?>) input; } else { clazz = input.getClass(); // Get class from the instance } if (Animal.class.isAssignableFrom(clazz)) { System.out.println("Matched " + clazz.getName()); } else { System.out.println("Unmatched " + clazz.getName()); } }
This way, both classType(animal) and classType(animal.getClass()) will work correctly.
Why Your Original Approach Is Risky
Using type.toString().contains("Animal") is fragile because:
- It can false-positive match other classes named
Animalin different packages. - Code obfuscation (common in production) will break this check.
- It's unclear to other developers what you're trying to validate.
内容的提问来源于stack exchange,提问作者Ashish

