fix(后端): 请求日志敏感信息脱敏
- 添加请求参数脱敏功能,对 password、token 等敏感字段进行掩码处理 - 响应结果中的敏感信息同样脱敏 - 敏感字段列表:password, pwd, secret, token, accessToken, refreshToken, oldPassword, newPassword, confirmPassword - 脱敏掩码:*** Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
79d98be366
commit
c73bae7104
@ -1,6 +1,7 @@
|
|||||||
package com.lesingle.edu.common.aspect;
|
package com.lesingle.edu.common.aspect;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.lesingle.edu.common.security.SecurityUtils;
|
import com.lesingle.edu.common.security.SecurityUtils;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -18,13 +19,26 @@ import java.util.Objects;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求日志切面
|
* 请求日志切面
|
||||||
* 记录所有 Controller 层的请求信息
|
* 记录所有 Controller 层的请求信息(敏感信息已脱敏)
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
public class RequestLogAspect {
|
public class RequestLogAspect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 敏感字段列表(不区分大小写)
|
||||||
|
*/
|
||||||
|
private static final String[] SENSITIVE_FIELDS = {
|
||||||
|
"password", "pwd", "secret", "token", "accessToken", "refreshToken",
|
||||||
|
"oldPassword", "newPassword", "confirmPassword"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏掩码
|
||||||
|
*/
|
||||||
|
private static final String MASK = "***";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义切点:Controller 层所有方法
|
* 定义切点:Controller 层所有方法
|
||||||
*/
|
*/
|
||||||
@ -62,7 +76,8 @@ public class RequestLogAspect {
|
|||||||
String methodType = request.getMethod();
|
String methodType = request.getMethod();
|
||||||
String className = joinPoint.getTarget().getClass().getName();
|
String className = joinPoint.getTarget().getClass().getName();
|
||||||
String methodName = method.getName();
|
String methodName = method.getName();
|
||||||
String params = JSON.toJSONString(getRequestParams(joinPoint));
|
// 请求参数脱敏处理
|
||||||
|
String params = desensitize(getRequestParams(joinPoint));
|
||||||
|
|
||||||
log.info("===== 请求开始 [userId={}, role={}] =====", userId, role);
|
log.info("===== 请求开始 [userId={}, role={}] =====", userId, role);
|
||||||
log.info("接口地址:{} {}", methodType, requestURI);
|
log.info("接口地址:{} {}", methodType, requestURI);
|
||||||
@ -77,7 +92,7 @@ public class RequestLogAspect {
|
|||||||
|
|
||||||
// 记录响应结果
|
// 记录响应结果
|
||||||
log.info("响应时间:{}ms", duration);
|
log.info("响应时间:{}ms", duration);
|
||||||
log.info("响应结果:{}", JSON.toJSONString(result));
|
log.info("响应结果:{}", desensitize(result));
|
||||||
log.info("===== 请求结束 [userId={}, role={}] =====", userId, role);
|
log.info("===== 请求结束 [userId={}, role={}] =====", userId, role);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -111,4 +126,72 @@ public class RequestLogAspect {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求参数脱敏处理
|
||||||
|
* 将敏感字段(如 password、token 等)替换为 ***
|
||||||
|
*
|
||||||
|
* @param original 原始参数对象
|
||||||
|
* @return 脱敏后的 JSON 字符串
|
||||||
|
*/
|
||||||
|
private String desensitize(Object original) {
|
||||||
|
if (original == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 转换为 JSONObject 以便修改
|
||||||
|
String jsonStr = JSON.toJSONString(original);
|
||||||
|
JSONObject jsonObject = JSON.parseObject(jsonStr);
|
||||||
|
if (jsonObject != null) {
|
||||||
|
maskSensitiveFields(jsonObject);
|
||||||
|
return jsonObject.toJSONString();
|
||||||
|
}
|
||||||
|
return jsonStr;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 解析失败时返回原始 JSON
|
||||||
|
log.warn("请求参数脱敏失败:{}", e.getMessage());
|
||||||
|
return JSON.toJSONString(original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归掩码敏感字段
|
||||||
|
*
|
||||||
|
* @param jsonObject 要处理的 JSON 对象
|
||||||
|
*/
|
||||||
|
private void maskSensitiveFields(JSONObject jsonObject) {
|
||||||
|
if (jsonObject == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String key : jsonObject.keySet()) {
|
||||||
|
if (isSensitiveField(key)) {
|
||||||
|
jsonObject.put(key, MASK);
|
||||||
|
} else {
|
||||||
|
// 递归处理嵌套的 JSONObject
|
||||||
|
Object value = jsonObject.get(key);
|
||||||
|
if (value instanceof JSONObject) {
|
||||||
|
maskSensitiveFields((JSONObject) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字段名是否为敏感字段
|
||||||
|
*
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @return true 如果是敏感字段
|
||||||
|
*/
|
||||||
|
private boolean isSensitiveField(String fieldName) {
|
||||||
|
if (fieldName == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String lowerFieldName = fieldName.toLowerCase();
|
||||||
|
for (String sensitiveField : SENSITIVE_FIELDS) {
|
||||||
|
if (lowerFieldName.equals(sensitiveField.toLowerCase())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user