
自定义 Retrofit 日志拦截器
AI-摘要
Tianli GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
本文最后更新于 2025-02-21,文章内容可能已经过时。
自定义 Retrofit 日志拦截器实现敏感信息脱敏
背景
在使用 Retrofit 进行 HTTP 请求时,我们经常需要打印请求和响应日志用于调试。但是日志中可能包含敏感信息(如密码、手机号、身份证等),直接打印可能会造成安全隐患。本文将介绍如何自定义 Retrofit 日志拦截器来实现敏感信息的脱敏处理。
实现目标
- 合并同一请求的请求和响应日志,使日志更清晰
- 对敏感信息进行脱敏处理:
- 手机号、身份证等信息:保留前后两位,中间用 * 代替
- 密码等高敏感信息:完全用 *** 代替
- 不影响实际的请求和响应数据,只对日志进行脱敏
核心代码实现
1. 配置类
首先创建一个配置类来设置日志属性:
@Configuration
public class RetrofitConfiguration {
@Bean
public GlobalLogProperty globalLogProperty() {
GlobalLogProperty property = new GlobalLogProperty();
property.setEnable(true);
property.setLogLevel(LogLevel.INFO);
property.setLogStrategy(LogStrategy.BODY);
return property;
}
}
2. 自定义日志拦截器
创建自定义日志拦截器类:
@Component
@Slf4j
public class CustomLoggingInterceptor extends LoggingInterceptor {
// 需要保留前后两位的字段
private static final List<String> MASK_FIELDS = new ArrayList<>(Arrays.asList(
"idNo", "idCard", "mobile", "phone", "bankCardNo"
// ... 更多字段
));
// 需要完全隐藏的字段
private static final List<String> HIDE_FIELDS = new ArrayList<>(Arrays.asList(
"password", "pin", "smscode"
// ... 更多字段
));
public CustomLoggingInterceptor(GlobalLogProperty globalLogProperty) {
super(globalLogProperty);
}
// ... 其他实现代码
}
关键功能实现
1. 日志合并
使用 Map 存储同一线程的日志信息,确保请求和响应日志合并显示:
@Override
protected HttpLoggingInterceptor.Logger matchLogger(LogLevel level) {
return new HttpLoggingInterceptor.Logger() {
private final Map<String, StringBuilder> requestMap = new HashMap<>();
private final String currentThread = Thread.currentThread().getName();
@Override
public void log(String message) {
try {
String logMessage = maskSensitiveInfo(message);
StringBuilder logBuilder = requestMap.computeIfAbsent(
currentThread, k -> new StringBuilder()
);
logBuilder.append(logMessage).append("\n");
// 响应结束时输出完整日志
if (message.startsWith("<-- END")) {
log.info("\n{}", logBuilder.toString());
requestMap.remove(currentThread);
}
} catch (Exception e) {
log.error("Log processing error:", e);
}
}
};
}
2. 敏感信息脱敏
实现脱敏逻辑,处理不同类型的敏感信息:
private String maskSensitiveInfo(String message) {
try {
// 处理需要保留前后两位的字段
for (String field : MASK_FIELDS) {
String regex = String.format("(\"%s\"\\s*:\\s*\")([^\"]*)(\")", field);
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
String value = matcher.group(2);
if (value == null || value.isEmpty()) {
continue;
}
// 处理不同长度的值
if (value.length() <= 4) {
// 短字符串全部打码
message = message.replace(matcher.group(0),
String.format("\"%s\":\"****\"", field));
} else {
// 保留前后两位
StringBuilder maskedValue = new StringBuilder()
.append(value.substring(0, 2))
.append("*".repeat(value.length() - 4))
.append(value.substring(value.length() - 2));
message = message.replace(matcher.group(0),
String.format("\"%s\":\"%s\"", field, maskedValue));
}
}
}
// 处理需要完全隐藏的字段
for (String field : HIDE_FIELDS) {
// ... 类似的处理逻辑,但使用 *** 替换整个值
}
} catch (Exception e) {
log.error("Mask sensitive info failed", e);
return message;
}
return message;
}
使用效果
脱敏后的日志示例:
--> POST http://api.example.com/login
Content-Type: application/json
{
"mobile": "13*******89",
"password": "***",
"idCard": "11**********33"
}
--> END POST
<-- 200 OK (136ms)
{
"code": 0,
"message": "success",
"data": {
"phone": "13*******89",
"name": "张三"
}
}
<-- END HTTP
注意事项
- 脱敏处理只影响日志输出,不会修改实际的请求和响应数据
- 需要及时清理日志 Map,避免内存泄漏
- 正则表达式的性能问题,建议预编译正则表达式
- 异常处理的完整性,确保日志拦截器不影响正常业务流程
总结
通过自定义 Retrofit 日志拦截器,我们实现了:
- 敏感信息的脱敏处理
- 统一的日志格式
- 更好的日志可读性
- 安全的信息展示
这个实现可以很好地平衡开发调试的便利性和信息安全的要求。您可以根据实际需求调整脱敏规则和日志格式。
这个教程文档包含了完整的实现思路、代码示例和注意事项。您可以根据需要进行修改和补充,添加更多的实际使用场景和注意事项。
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 JerryStack
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果