package com.dsh.guns.core.aop;
|
|
import com.dsh.guns.config.UserExt;
|
import com.dsh.guns.core.common.annotion.BussinessLog;
|
import com.dsh.guns.core.common.constant.dictmap.base.AbstractDictMap;
|
import com.dsh.guns.core.log.LogManager;
|
import com.dsh.guns.core.log.LogObjectHolder;
|
import com.dsh.guns.core.log.factory.LogTaskFactory;
|
import com.dsh.guns.core.support.HttpKit;
|
import com.dsh.guns.core.util.Contrast;
|
import com.dsh.guns.modular.system.audit.ManagementAuditWriter;
|
import com.dsh.guns.modular.system.audit.AuditDataFormatter;
|
import com.dsh.guns.modular.system.model.User;
|
import com.dsh.guns.modular.system.service.IUserService;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.Signature;
|
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Aspect;
|
import org.aspectj.lang.annotation.Pointcut;
|
import org.aspectj.lang.reflect.MethodSignature;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.stereotype.Component;
|
|
import javax.annotation.Resource;
|
import javax.servlet.http.HttpServletRequest;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.lang.reflect.Method;
|
import java.util.Map;
|
import java.util.UUID;
|
|
/**
|
* 日志记录
|
*
|
* @author fengshuonan
|
* @date 2016年12月6日 下午8:48:30
|
*/
|
@Aspect
|
@Component
|
public class LogAop {
|
|
private Logger log = LoggerFactory.getLogger(this.getClass());
|
@Resource private ManagementAuditWriter auditWriter;
|
@Resource private IUserService userService;
|
|
@Pointcut(value = "@annotation(com.dsh.guns.core.common.annotion.BussinessLog)")
|
public void cutService() {
|
}
|
|
@Around("cutService()")
|
public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
|
String beforeData = beforeData();
|
try {
|
Object result = point.proceed();
|
appendUnifiedAudit(point, "SUCCESS", null, beforeData);
|
return result;
|
} catch (Throwable e) {
|
appendUnifiedAudit(point, "FAIL", e.getMessage(), beforeData);
|
throw e;
|
}
|
}
|
|
/**
|
* @BussinessLog 是后台已有的“需要记录的用户操作”白名单。
|
* 在这里写统一审计,能够覆盖 /mgr/add 等直接访问管理端的请求,
|
* 并避免将其内部 Feign 调用和 POST 查询误记为操作。
|
*/
|
private void appendUnifiedAudit(ProceedingJoinPoint point, String result, String errorMessage, String beforeData) {
|
try {
|
Method method = ((MethodSignature) point.getSignature()).getMethod();
|
BussinessLog annotation = method.getAnnotation(BussinessLog.class);
|
User user = UserExt.getUser();
|
if (annotation == null || user == null) return;
|
|
HttpServletRequest request = HttpKit.getRequest();
|
Map<String, Object> audit = new HashMap<String, Object>();
|
audit.put("traceId", UUID.randomUUID().toString().replace("-", ""));
|
audit.put("sourceService", "mb-cloud-management");
|
audit.put("sourceType", "HTTP");
|
audit.put("operationType", operationType(annotation.value()));
|
audit.put("riskLevel", "NORMAL");
|
audit.put("operationName", annotation.value());
|
audit.put("requestMethod", request.getMethod());
|
audit.put("requestUri", request.getRequestURI());
|
// 审计表字段为 varchar,必须在调用审计服务前序列化;直接传 Map 会导致
|
// Spring 无法绑定到 SysAccountAuditLog.requestParams(String),从而整条日志写入失败。
|
audit.put("requestParams", AuditDataFormatter.format(maskedParameters(HttpKit.getRequestParameters()), User.class));
|
audit.put("beforeData", beforeData);
|
audit.put("afterData", afterData(beforeData));
|
audit.put("userId", user.getId());
|
audit.put("userAccount", user.getAccount());
|
audit.put("clientIp", HttpKit.getIp());
|
audit.put("result", result);
|
audit.put("errorMessage", maskText(errorMessage));
|
audit.put("createdAt", new Date());
|
auditWriter.append(audit);
|
} catch (Exception auditException) {
|
// 审计失败不能影响原有业务。
|
log.error("统一操作审计记录出错", auditException);
|
}
|
}
|
|
private String operationType(String operationName) {
|
String value = operationName == null ? "" : operationName;
|
if (value.contains("删除")) return "DELETE";
|
if (value.contains("添加") || value.contains("新增")) return "CREATE";
|
if (value.contains("冻结") || value.contains("解冻") || value.contains("审核") || value.contains("状态")) return "STATUS_CHANGE";
|
return "UPDATE";
|
}
|
|
private Map<String, String> maskedParameters(Map<String, String> parameters) {
|
Map<String, String> masked = new HashMap<String, String>();
|
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
String key = entry.getKey();
|
masked.put(key, key != null && key.toLowerCase().matches(".*(password|token|secret|idcard|identity|bank|card|mobile|phone).*")
|
? "***" : entry.getValue());
|
}
|
return masked;
|
}
|
|
private String maskText(String text) {
|
return text == null ? null : text.replaceAll("(?i)(password|token|authorization|idcard|bankCard)\\s*[:=]\\s*[^,\\s&}]+", "$1=***");
|
}
|
|
/** 员工修改前后均从数据库读取真实记录,避免将前端提交值误当作旧值。 */
|
private String beforeData() {
|
try {
|
HttpServletRequest request = HttpKit.getRequest();
|
if (!"/mgr/edit".equals(request.getRequestURI())) return null;
|
Integer userId = Integer.valueOf(request.getParameter("id"));
|
return userSnapshot(userService.getById(userId));
|
} catch (Exception ignored) {
|
return null;
|
}
|
}
|
|
private String afterData(String beforeData) {
|
if (beforeData == null) return null;
|
try {
|
Integer userId = Integer.valueOf(HttpKit.getRequest().getParameter("id"));
|
return userSnapshot(userService.getById(userId));
|
} catch (Exception ignored) {
|
return null;
|
}
|
}
|
|
private String userSnapshot(User target) {
|
if (target == null) return null;
|
Map<String, Object> data = new HashMap<String, Object>();
|
data.put("id", target.getId());
|
data.put("account", target.getAccount());
|
data.put("name", target.getName());
|
data.put("phone", target.getPhone());
|
data.put("birthday", target.getBirthday());
|
data.put("sex", target.getSex());
|
data.put("email", target.getEmail());
|
data.put("roleid", target.getRoleid());
|
data.put("deptid", target.getDeptid());
|
data.put("status", target.getStatus());
|
data.put("objectType", target.getObjectType());
|
data.put("objectId", target.getObjectId());
|
data.put("idCard", target.getIdCard());
|
data.put("avatar", target.getAvatar());
|
return AuditDataFormatter.format(data, User.class);
|
}
|
|
private void handle(ProceedingJoinPoint point) throws Exception {
|
|
//获取拦截的方法名
|
Signature sig = point.getSignature();
|
MethodSignature msig = null;
|
if (!(sig instanceof MethodSignature)) {
|
throw new IllegalArgumentException("该注解只能用于方法");
|
}
|
msig = (MethodSignature) sig;
|
Object target = point.getTarget();
|
Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
|
String methodName = currentMethod.getName();
|
|
//如果当前用户未登录,不做日志
|
User user = UserExt.getUser();
|
if (null == user) {
|
return;
|
}
|
|
//获取拦截方法的参数
|
String className = point.getTarget().getClass().getName();
|
Object[] params = point.getArgs();
|
|
//获取操作名称
|
BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
|
String bussinessName = annotation.value();
|
String key = annotation.key();
|
Class dictClass = annotation.dict();
|
|
StringBuilder sb = new StringBuilder();
|
for (Object param : params) {
|
sb.append(param);
|
sb.append(" & ");
|
}
|
|
//如果涉及到修改,比对变化
|
String msg;
|
if (bussinessName.indexOf("修改") != -1 || bussinessName.indexOf("编辑") != -1) {
|
Object obj1 = LogObjectHolder.me().get();
|
Map<String, String> obj2 = HttpKit.getRequestParameters();
|
msg = Contrast.contrastObj(dictClass, key, obj1, obj2);
|
} else {
|
Map<String, String> parameters = HttpKit.getRequestParameters();
|
AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance();
|
msg = Contrast.parseMutiKey(dictMap,key,parameters);
|
}
|
|
// LogManager.me().executeLog(LogTaskFactory.bussinessLog(1, bussinessName, className, methodName, msg));
|
|
}
|
}
|