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 audit = new HashMap(); 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)); String afterData = afterData(beforeData); AuditDataFormatter.ChangeSet changes = AuditDataFormatter.changes(beforeData, afterData); audit.put("beforeData", changes.getBefore()); audit.put("afterData", changes.getAfter()); 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 maskedParameters(Map parameters) { Map masked = new HashMap(); for (Map.Entry 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 data = new HashMap(); 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 obj2 = HttpKit.getRequestParameters(); msg = Contrast.contrastObj(dictClass, key, obj1, obj2); } else { Map parameters = HttpKit.getRequestParameters(); AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance(); msg = Contrast.parseMutiKey(dictMap,key,parameters); } // LogManager.me().executeLog(LogTaskFactory.bussinessLog(1, bussinessName, className, methodName, msg)); } }