|
@@ -0,0 +1,235 @@
|
|
|
+package com.szwl.aspect;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.szwl.annotation.Audit;
|
|
|
+import com.szwl.manager.TokenManager;
|
|
|
+import com.szwl.model.bo.UserDetailBO;
|
|
|
+import com.szwl.model.entity.AuditLog;
|
|
|
+import com.szwl.service.AuditLogService;
|
|
|
+import com.szwl.util.SpringUtils;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.exception.ExceptionUtils;
|
|
|
+import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
+import org.aspectj.lang.annotation.Around;
|
|
|
+import org.aspectj.lang.annotation.Aspect;
|
|
|
+import org.aspectj.lang.reflect.MethodSignature;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.context.expression.BeanFactoryResolver;
|
|
|
+import org.springframework.core.DefaultParameterNameDiscoverer;
|
|
|
+import org.springframework.core.ParameterNameDiscoverer;
|
|
|
+import org.springframework.expression.EvaluationContext;
|
|
|
+import org.springframework.expression.Expression;
|
|
|
+import org.springframework.expression.ExpressionParser;
|
|
|
+import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|
|
+import org.springframework.expression.spel.support.StandardEvaluationContext;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.LinkedHashMap;
|
|
|
+import java.util.concurrent.LinkedBlockingQueue;
|
|
|
+import java.util.concurrent.ThreadPoolExecutor;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author PT-ZHOUYUHENG
|
|
|
+ * @date 2022-05-17 11:45
|
|
|
+ */
|
|
|
+@Aspect
|
|
|
+@Component
|
|
|
+@Slf4j
|
|
|
+public class AuditAspect {
|
|
|
+ @Autowired
|
|
|
+ AuditLogService auditLogService;
|
|
|
+ @Autowired
|
|
|
+ private TokenManager securityManager;
|
|
|
+
|
|
|
+ @Value("${spring.application.name}")
|
|
|
+ private String appName;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析SPEL,例如#obj.name
|
|
|
+ */
|
|
|
+ private final ExpressionParser parser = new SpelExpressionParser();
|
|
|
+ private final String spElFlag = "#";
|
|
|
+ private static final BeanFactoryResolver BEAN_FACTORY_RESOLVER = new BeanFactoryResolver(SpringUtils.getApplicationContext());
|
|
|
+
|
|
|
+ private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(4, 8, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(500));
|
|
|
+
|
|
|
+
|
|
|
+ @Around(value = "@annotation(com.szwl.annotation.Audit)")
|
|
|
+ public Object around(ProceedingJoinPoint point) throws Throwable {
|
|
|
+ boolean success = false;
|
|
|
+ Exception exception = null;
|
|
|
+ Object[] reqValue = null;
|
|
|
+ Object respValue = null;
|
|
|
+ try {
|
|
|
+ Object[] args = point.getArgs();
|
|
|
+ reqValue = args;
|
|
|
+ Object result = point.proceed(args);
|
|
|
+ respValue = result;
|
|
|
+ success = true;
|
|
|
+ return result;
|
|
|
+ } catch (Exception e) {
|
|
|
+ exception = e;
|
|
|
+ throw e;
|
|
|
+ } finally {
|
|
|
+ UserDetailBO userDetails;
|
|
|
+ try {
|
|
|
+ userDetails = securityManager.getLoginUserDetails();
|
|
|
+ } catch (Exception e) {
|
|
|
+ userDetails = new UserDetailBO();
|
|
|
+ }
|
|
|
+ EXECUTOR.execute(new AuditThread(userDetails, success, point, exception, reqValue, respValue));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getReqValue(Object[] args) {
|
|
|
+ try {
|
|
|
+ return JSON.toJSONString(args);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("--解析请求参数失败:{}", e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getRespValue(Object result) {
|
|
|
+ try {
|
|
|
+ return JSON.toJSONString(result);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("--解析响应参数失败:{}", e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建审计日志
|
|
|
+ *
|
|
|
+ * @param userDetails
|
|
|
+ * @param success
|
|
|
+ * @param point
|
|
|
+ * @param exception
|
|
|
+ */
|
|
|
+ private void buildAuditLog(UserDetailBO userDetails, boolean success, ProceedingJoinPoint point, Exception exception, Object[] reqValue, Object respValue) {
|
|
|
+ if(null == userDetails){
|
|
|
+ userDetails = new UserDetailBO();
|
|
|
+ userDetails.setUsername("none");
|
|
|
+ userDetails.setName("none");
|
|
|
+ }
|
|
|
+ MethodSignature signature = (MethodSignature) point.getSignature();
|
|
|
+ Method method = signature.getMethod();
|
|
|
+ Audit audit = method.getAnnotation(Audit.class);
|
|
|
+ if (audit == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //求值上下文
|
|
|
+ StandardEvaluationContext context = new StandardEvaluationContext();
|
|
|
+ //设置参数进去
|
|
|
+ context.setVariables(resolveArgs(point));
|
|
|
+ //给定特定的参数
|
|
|
+ context.setVariable("loginUser", userDetails);
|
|
|
+ context.setVariable("returnObj", respValue);
|
|
|
+ context.setBeanResolver(BEAN_FACTORY_RESOLVER);
|
|
|
+ //构建log对象
|
|
|
+ AuditLog logParam = new AuditLog();
|
|
|
+ try {
|
|
|
+ logParam.setBizNo(resolveValue(audit.bizNo(), context));
|
|
|
+ } catch (Exception e) {
|
|
|
+ logParam.setBizNo(String.format("SPEL-ERROR bizNo[%s]失败,原因:%s", audit.bizNo(), e.getMessage()));
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ logParam.setContent(resolveValue(audit.content(), context));
|
|
|
+ } catch (Exception e) {
|
|
|
+ logParam.setContent(String.format("SPEL-ERROR content[%s]失败,原因:%s", audit.content(), e.getMessage()));
|
|
|
+ }
|
|
|
+ logParam.setType(audit.type().getCode());
|
|
|
+ logParam.setCreateTime(new Date());
|
|
|
+ logParam.setMethod(method.getDeclaringClass().getName() + "#" + method.getName());
|
|
|
+ logParam.setSuccess(success);
|
|
|
+ logParam.setException(exception != null && !success ? ExceptionUtils.getStackTrace(exception) : null);
|
|
|
+ logParam.setReqValue(getReqValue(reqValue));
|
|
|
+ logParam.setRespValue(getRespValue(respValue));
|
|
|
+ logParam.setOperatorUserName(userDetails.getUsername());
|
|
|
+ logParam.setOperatorUserRealName(userDetails.getName());
|
|
|
+ logParam.setOperatorIp(userDetails.getCurrentIp());
|
|
|
+ logParam.setOwnerSys(appName);
|
|
|
+ log.info("--生成的审计日志:{}", JSON.toJSONString(logParam));
|
|
|
+ auditLogService.save(logParam);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析参数
|
|
|
+ *
|
|
|
+ * @param point
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private LinkedHashMap<String, Object> resolveArgs(ProceedingJoinPoint point) {
|
|
|
+ MethodSignature signature = (MethodSignature) point.getSignature();
|
|
|
+ Method method = signature.getMethod();
|
|
|
+ Object[] args = point.getArgs();
|
|
|
+ String[] argNames = getArgNames(method);
|
|
|
+ LinkedHashMap<String, Object> params = new LinkedHashMap<>();
|
|
|
+ for (int i = 0; i < args.length; i++) {
|
|
|
+ params.put(argNames[i], args[i]);
|
|
|
+ }
|
|
|
+ return params;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取参数属性名
|
|
|
+ *
|
|
|
+ * @param method
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private String[] getArgNames(Method method) {
|
|
|
+ ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
|
|
+ return parameterNameDiscoverer.getParameterNames(method);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveValue(String content, EvaluationContext context) {
|
|
|
+ String value;
|
|
|
+ if (content.contains(spElFlag)) {
|
|
|
+ //解析SPEL
|
|
|
+ value = resolveValueByExpression(content, context);
|
|
|
+ } else {
|
|
|
+ //不处理
|
|
|
+ value = content;
|
|
|
+ }
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String resolveValueByExpression(String spElString, EvaluationContext context) {
|
|
|
+ //构建表达式
|
|
|
+ Expression expression = parser.parseExpression(spElString);
|
|
|
+ //解析
|
|
|
+ return expression.getValue(context, String.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ class AuditThread implements Runnable {
|
|
|
+ private UserDetailBO userDetails;
|
|
|
+ private boolean success;
|
|
|
+ private ProceedingJoinPoint point;
|
|
|
+ private Exception exception;
|
|
|
+ private Object[] reqValue;
|
|
|
+ private Object respValue;
|
|
|
+
|
|
|
+ public AuditThread(UserDetailBO userDetails, boolean success, ProceedingJoinPoint point, Exception exception, Object[] reqValue, Object respValue) {
|
|
|
+ this.userDetails = userDetails;
|
|
|
+ this.success = success;
|
|
|
+ this.point = point;
|
|
|
+ this.exception = exception;
|
|
|
+ this.reqValue = reqValue;
|
|
|
+ this.respValue = respValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ try {
|
|
|
+ buildAuditLog(userDetails, success, point, exception, reqValue, respValue);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("--审计日志记录失败:", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|