All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cn.watsontech.webhelper.common.aop.AccessLogAspect Maven / Gradle / Ivy
package cn.watsontech.webhelper.common.aop;
import cn.watsontech.webhelper.common.aop.annotation.Access;
import cn.watsontech.webhelper.common.aop.annotation.AccessParam;
import cn.watsontech.webhelper.common.aop.entity.AccessLog;
import cn.watsontech.webhelper.common.security.LoginUser;
import cn.watsontech.webhelper.common.security.authentication.AccountService;
import cn.watsontech.webhelper.common.util.RequestUtils;
import cn.watsontech.webhelper.utils.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringValueResolver;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 系统日志切点类
*
* Created by Watson on 2020/02/09.
*/
@Aspect
@Component
public class AccessLogAspect implements EmbeddedValueResolverAware {
protected static final Log log = LogFactory.getLog(AccessLogAspect.class);
/**
* 本地线程日志
*/
private static final ThreadLocal logThreadLocal = new NamedThreadLocal("AccessLogAspectThreadLocal");
/**
* 线程池任务执行程序
*/
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
/**
* 日志储存service
*/
@Autowired
private LogService logService;
@Nullable
private StringValueResolver embeddedValueResolver;
/**
* Controller层切点 注解拦截
*/
@Pointcut("@annotation(cn.watsontech.webhelper.common.aop.annotation.Access)")
public void accessLogPointCut() {
log.info("+++++++++++++Controller层切点 注解拦截+++++++++++++");
}
/**
* 环绕通知 用于拦截Controller层记录用户的操作的开始时间
*
* @param joinPoint 切点
*/
@Around("accessLogPointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = null;
logThreadLocal.set(System.currentTimeMillis());
result = joinPoint.proceed();
LoginUser user = getCurrentLoginUser();
if (user==null) {
org.aspectj.lang.Signature signature = joinPoint.getSignature();
//检查是否是登陆接口
if (signature.getDeclaringType()==AccountService.class) {
if (signature.getName().startsWith("loginBy")) {
if (result instanceof LoginUser) {
user = (LoginUser) result;
}
}
}
}
saveLog(joinPoint, null, user);
return result;
}
private void saveLog(JoinPoint joinPoint, Throwable exception, LoginUser user) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Access access = AnnotationUtils.findAnnotation(method, Access.class);
AccessLog testLog = wrapAccessLog(method, joinPoint.getArgs(), access, exception, user);
// 1.直接执行保存操作
// this.logService.createSystemLog(log);
// 2.优化:异步保存日志
// new SaveLogThread(log, logService).start();
// 3.再优化:通过线程池来执行日志保存
threadPoolTaskExecutor.execute(new SaveLogThread(testLog, isAccessShouldSave(access)));
}
// 读取session中的用户
private LoginUser getCurrentLoginUser() {
LoginUser user = null;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication!=null) {
Object principal = authentication.getPrincipal();
if (principal!=null) {
if (principal instanceof LoginUser) {
user = (LoginUser) principal;
}
}
}
return user;
}
private AccessLog wrapAccessLog(Method signatureMethod, Object[] methodArgs, Access accessAnnotation, Throwable exception, LoginUser user) {
HttpServletRequest request = RequestUtils.getHttpServletRequest();
String remoteAddr = RequestUtils.getIpAddress(request);// 请求的IP
String requestBrowser = RequestUtils.getBrowser(request);// 请求的浏览器类型
String requestUri = request.getRequestURI();// 请求的Uri
String method = request.getMethod(); // 请求的方法类型(post/get)
AccessParamValue accessParamValue = null;
String levelType = accessAnnotation.level();
String tag = accessAnnotation.tag();
String exMessage = null;
if (exception!=null) {
levelType = "error";
exMessage = exception.getMessage();
if (exMessage==null) {
exMessage = exception.toString();
}
}
if (levelType!=null&&embeddedValueResolver!=null) {
levelType = embeddedValueResolver.resolveStringValue(levelType);
}
try {
accessParamValue = getControllerMethodDescription(signatureMethod, methodArgs, accessAnnotation);
} catch (Exception e) {
e.printStackTrace();
accessParamValue = new AccessParamValue("未知",null, null);
}
AccessLog testLog = new AccessLog();
testLog.setLevel(levelType);
testLog.setTag(tag);
testLog.setIp(remoteAddr);
testLog.setUrl(requestUri);
testLog.setBrowser(requestBrowser);
testLog.setMethod(method);
testLog.setException(exMessage);
if (accessParamValue!=null) {
testLog.setTitle(accessParamValue.getDescription());
testLog.setGroupId(accessParamValue.getGroupId());
testLog.setGroupField(accessParamValue.getGroupField());
}
testLog.setVersion(1);
testLog.setParams(getParameters(signatureMethod, methodArgs));
if (user!=null) {
testLog.setCreatedBy(user.getId());
testLog.setCreatedByName(user.getFullName());
}
testLog.setCreatedTime(new Date());
if (logThreadLocal.get()!=null) {
testLog.setTotalTimes(System.currentTimeMillis()-logThreadLocal.get());
}
return testLog;
}
/**
* 根据方法和传入的参数获取请求参数
*/
private String getParameters(Method method, Object[] args) {
List argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//将RequestBody注解修饰的参数作为请求参数
Object argsI = args[i];
if (argsI!=null) {
//修复 序列化异常,过滤httpServletRequest和response参数
if (argsI instanceof HttpServletRequest || argsI instanceof HttpServletResponse) {
continue;
}
if (args[i]==null) continue;
else if (args[i] instanceof Number||args[i] instanceof String||args[i] instanceof Boolean) argsI = args[i];
else if (args[i] instanceof MultipartFile) argsI = ((MultipartFile) args[i]).getName();
else
//清楚记录里的password
try {
argsI = JSON.toJSON(args[i]);
if (argsI instanceof JSONObject) {
((JSONObject)argsI).replace("password", "*****");
((JSONObject)argsI).replace("oldPassword", "*****");
((JSONObject)argsI).replace("confirmPassword", "*****");
}
}catch (Exception ex) {ex.printStackTrace();}
}
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
//将RequestParam注解修饰的参数作为请求参数
RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
if (requestBody != null) {
argList.add(argsI);
}else if (requestParam != null) {
Map map = new HashMap<>();
String key = parameters[i].getName();
if (!StringUtils.isEmpty(requestParam.value())) {
key = requestParam.value();
}
map.put(key, argsI);
argList.add(map);
}
}
if (!CollectionUtils.isEmpty(argList)) {
if (argList.size()==1) {
return JSON.toJSONString(argList.get(0));
}
return JSON.toJSONString(argList);
}
return "";
}
/**
* 异常通知
*/
@AfterThrowing(pointcut = "accessLogPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
LoginUser user = getCurrentLoginUser();
saveLog(joinPoint, e, user);
}
private boolean isAccessShouldSave(Access access) {
boolean result = false;
if (access!=null) {
String saveValue = access.save();
//支持可配置log语句
if (this.embeddedValueResolver != null) {
saveValue = this.embeddedValueResolver.resolveStringValue(saveValue);
}
try {
result = Boolean.parseBoolean(saveValue);
} catch (Exception ex) {//eat it}
}
}
return result;
}
static class AccessParamValue {
String description;
String groupId;
String groupField;
public AccessParamValue(String description, String groupId, String groupField) {
this.description = description;
this.groupId = groupId;
this.groupField = groupField;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getGroupField() {
return groupField;
}
public void setGroupField(String groupField) {
this.groupField = groupField;
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* 注意:如果有多个参数被标记为groupId但话,groupField和groupId为最后一个生效
* @param method 方法
* @return 方法描述
*/
private AccessParamValue getControllerMethodDescription(Method method, Object[] joinPointArgs, Access logAnnotation) {
String description = method.getName();
Object logGroupId = null;
String logGroupField = null;
Object[] descriptionParams = new Object[]{};
if (logAnnotation !=null) {
description = logAnnotation.description();
//支持可配置log语句
if (this.embeddedValueResolver != null) {
description = this.embeddedValueResolver.resolveStringValue(description);
}
Parameter[] params = method.getParameters();
if (params!=null) {
List tempDescParams = new ArrayList<>();
Object logParamValue = null;
String[] logParamFieldes = null;
Object logArg = null;
for (int i = 0; i < params.length; i++) {
AccessParam logParam = AnnotationUtils.findAnnotation(params[i], AccessParam.class);
if (logParam!=null) {
logArg = joinPointArgs[i];
logParamFieldes = logParam.fields();
if (logParamFieldes!=null&&logParamFieldes.length>0) {
String groupField = logParam.groupIdField();
for (int j = 0; j < logParamFieldes.length; j++) {
try {
logParamValue = getFieldValue(logArg,logParamFieldes[j]);
} catch (IllegalAccessException e) {
e.printStackTrace();
logParamValue = "未知";
} catch (InvocationTargetException e) {
e.printStackTrace();
logParamValue = "未知";
}
tempDescParams.add(logParamValue);
if (groupField!=null&&groupField.equalsIgnoreCase(logParamFieldes[j])) {
//更新groupId和groupField
logGroupId = logParamValue;
logGroupField = groupField;
}
}
}else {
tempDescParams.add(logArg);
if (logParam.isGroupId()) {
//若是组id,则设置logGroupId
logGroupId = logArg;
logGroupField = logParam.groupIdField();
}
}
}
}
if (tempDescParams.size()>0) {
descriptionParams = new Object[tempDescParams.size()];
tempDescParams.toArray(descriptionParams);
}
}
}
return new AccessParamValue(descriptionParams!=null&&descriptionParams.length>0?String.format(description, descriptionParams):description, logGroupId!=null?logGroupId.toString():null, logGroupField);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
/**
* 保存日志线程
*/
private class SaveLogThread implements Runnable {
private final AccessLog testLog;
private final boolean save;
public SaveLogThread(AccessLog testLog, boolean saveDb) {
this.testLog = testLog;
this.save = saveDb;
}
@Override
public void run() {
if (save) {
if (testLog!=null) {
if (testLog.getId()!=null) {
logService.update(testLog.getId(), testLog);
}else {
logService.save(testLog);
}
}
}
log.info(String.format("访问日志:%s", testLog));
}
}
//属性get方法缓存
static Map cachedFieldGetMethod = new HashMap<>();
/**
* 根据属性,获取get方法
* @param obj 对象
* @param name 属性名
* @return 该属性值
* @throws Exception
*/
private Object getFieldValue(Object obj, String name) throws InvocationTargetException, IllegalAccessException {
Class claz = obj.getClass();
String cachedFieldKey = claz.getName()+":"+name;
Method result = cachedFieldGetMethod.get(cachedFieldKey);
if (result==null) {
Method[] m = claz.getMethods();
for(int i = 0;i < m.length;i++){
if(("get"+name).toLowerCase().equals(m[i].getName().toLowerCase())){
result = m[i];
}
}
if (result!=null) {
cachedFieldGetMethod.put(cachedFieldKey, result);
}
}
return result!=null?result.invoke(obj):null;
}
}