| | |
| | | package com.gkhy.exam.framework.aspectj; |
| | | |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.hutool.core.util.URLUtil; |
| | | import cn.hutool.extra.servlet.ServletUtil; |
| | | import cn.hutool.json.JSONObject; |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.gkhy.exam.common.annotation.Log; |
| | | import com.gkhy.exam.common.domain.entity.SysUser; |
| | | import com.gkhy.exam.common.enums.BusinessStatus; |
| | | import com.gkhy.exam.common.filter.PropertyPreExcludeFilter; |
| | | import com.gkhy.exam.common.utils.SecurityUtils; |
| | | import com.gkhy.exam.common.utils.ServletUtils; |
| | | import com.gkhy.exam.common.utils.StringUtils; |
| | | import com.gkhy.exam.system.domain.SysOperLog; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.ArrayUtils; |
| | | import org.aspectj.lang.JoinPoint; |
| | | import org.aspectj.lang.annotation.AfterReturning; |
| | | import org.aspectj.lang.ProceedingJoinPoint; |
| | | import org.aspectj.lang.Signature; |
| | | import org.aspectj.lang.annotation.AfterThrowing; |
| | | import org.aspectj.lang.annotation.Around; |
| | | import org.aspectj.lang.annotation.Aspect; |
| | | import org.aspectj.lang.annotation.Before; |
| | | import org.aspectj.lang.annotation.Pointcut; |
| | | import org.aspectj.lang.reflect.MethodSignature; |
| | | import org.springframework.core.NamedThreadLocal; |
| | | import org.springframework.http.HttpMethod; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.validation.BindingResult; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.context.request.RequestContextHolder; |
| | | import org.springframework.web.context.request.ServletRequestAttributes; |
| | | |
| | | import javax.servlet.ServletRequest; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.lang.reflect.Method; |
| | | import java.lang.reflect.Parameter; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | |
| | | public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; |
| | | private static final ThreadLocal<Long> TIME_THREADLOCAL=new NamedThreadLocal<>("Cost Time"); |
| | | |
| | | /** |
| | | * 处理请求前执行 |
| | | * @param joinPoint |
| | | * @param controllerLog |
| | | */ |
| | | @Before(value = "@annotation(controllerLog)") |
| | | public void doBefore(JoinPoint joinPoint, Log controllerLog){ |
| | | TIME_THREADLOCAL.set(System.currentTimeMillis()); |
| | | @Pointcut("execution(public * com.gkhy.exam.*.controller..*.*(..))") |
| | | public void logPointCut(){ |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * 处理完请求后执行 |
| | | * @param joinPoint |
| | | * @param controllerLog |
| | | * @param jsonResult |
| | | * |
| | | * @param joinPoint 切点 |
| | | */ |
| | | @AfterReturning(pointcut = "@annotation(controllerLog)",returning ="jsonResult" ) |
| | | public void doAfterReturning(JoinPoint joinPoint,Log controllerLog,Object jsonResult){ |
| | | handleLog(joinPoint,controllerLog,null,jsonResult); |
| | | @Around("logPointCut()") |
| | | public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{ |
| | | SysUser user= SecurityUtils.getLoginUserWithoutError()!=null?SecurityUtils.getLoginUserWithoutError().getUser():null; |
| | | long startTime = System.currentTimeMillis(); |
| | | //获取当前请求对象 |
| | | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
| | | HttpServletRequest request = attributes.getRequest(); |
| | | Signature signature = joinPoint.getSignature(); |
| | | MethodSignature methodSignature = (MethodSignature) signature; |
| | | Method method = methodSignature.getMethod(); |
| | | StringBuffer requestURL = request.getRequestURL(); |
| | | JSONObject webLog = new JSONObject(); |
| | | String urlStr = request.getRequestURL().toString(); |
| | | webLog.put("basePath", StringUtils.removeSuffix(urlStr, URLUtil.url(urlStr).getPath())); |
| | | webLog.put("ip", ServletUtil.getClientIP(request,null)); |
| | | webLog.put("method",request.getMethod()); |
| | | Object params=getParameter(method, joinPoint.getArgs()); |
| | | |
| | | webLog.put("parameter",params); |
| | | webLog.put("uri",request.getRequestURI()); |
| | | webLog.put("url",requestURL.toString()); |
| | | if(user!=null) { |
| | | webLog.put("userName", user.getName()); |
| | | } |
| | | log.info(webLog.toString()); |
| | | Object result = joinPoint.proceed(); |
| | | if (result == null) { |
| | | //如果切到了 没有返回类型的void方法,这里直接返回 |
| | | return null; |
| | | } |
| | | long endTime = System.currentTimeMillis(); |
| | | webLog.put("result",StringUtils.sub(JSON.toJSONString(result),0,2000)); |
| | | webLog.put("spendTime",endTime - startTime); |
| | | log.info(webLog.toString()); |
| | | return result; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * 拦截异常操作 |
| | | * @param joinPoint |
| | | * @param controllerLog |
| | | * @param e |
| | | */ |
| | | @AfterThrowing(value = "@annotation(controllerLog)",throwing = "e") |
| | | public void doAfterThrowing(JoinPoint joinPoint,Log controllerLog,Exception e){ |
| | | handleLog(joinPoint,controllerLog,e,null); |
| | | @AfterThrowing(value = "logPointCut()", throwing = "e") |
| | | public void doAfterThrowing(JoinPoint joinPoint,Exception e){ |
| | | //获取当前请求对象 |
| | | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); |
| | | HttpServletRequest request = attributes.getRequest(); |
| | | String urlStr = request.getRequestURL().toString(); |
| | | log.error("@AfterThrowing异常通知:url={},出错了error_message={}", urlStr,e.getMessage()); |
| | | } |
| | | |
| | | protected void handleLog(final JoinPoint joinPoint,Log controllerLog,final Exception e,Object jsonResult){ |
| | | try{ |
| | | HttpServletRequest request= ServletUtils.getRequest(); |
| | | SysUser user= SecurityUtils.getLoginUser().getUser(); |
| | | SysOperLog operLog=new SysOperLog(); |
| | | operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); |
| | | |
| | | String ip= cn.hutool.extra.servlet.ServletUtil.getClientIP(request); |
| | | operLog.setOperIp(ip); |
| | | operLog.setOperUrl(StringUtils.sub(request.getRequestURI(),0,255)); |
| | | if(user!=null){ |
| | | operLog.setOperName(user.getUsername()); |
| | | } |
| | | if(e!=null){ |
| | | operLog.setStatus(BusinessStatus.FAIL.ordinal()); |
| | | operLog.setErrorMsg(StringUtils.sub(e.getMessage(),0,2000)); |
| | | } |
| | | String className=joinPoint.getTarget().getClass().getName(); |
| | | String methodName=joinPoint.getSignature().getName(); |
| | | operLog.setMethod(className+"."+methodName+"()"); |
| | | operLog.setRequestMethod(request.getMethod()); |
| | | getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); |
| | | operLog.setCostTime(System.currentTimeMillis()-TIME_THREADLOCAL.get()); |
| | | log.info(JSON.toJSONString(operLog)); |
| | | // AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); |
| | | }catch (Exception exp){ |
| | | log.error("异常信息:{}",exp.getMessage()); |
| | | exp.printStackTrace(); |
| | | }finally { |
| | | TIME_THREADLOCAL.remove(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取注解中对方法的描述信息 用于Controller层注解 |
| | | * |
| | | * @param log 日志 |
| | | * @param operLog 操作日志 |
| | | * @throws Exception |
| | | * 根据方法和传入的参数获取请求参数 |
| | | */ |
| | | public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception |
| | | { |
| | | // 设置action动作 |
| | | operLog.setBusinessType(log.businessType().ordinal()); |
| | | // 设置标题 |
| | | operLog.setTitle(log.title()); |
| | | // 设置操作人类别 |
| | | operLog.setOperatorType(log.operatorType().ordinal()); |
| | | // 是否需要保存request,参数和值 |
| | | if (log.isSaveRequestData()) |
| | | { |
| | | // 获取参数的信息,传入到数据库中。 |
| | | setRequestValue(joinPoint, operLog, log.excludeParamNames()); |
| | | private Object getParameter(Method method, Object[] args) { |
| | | List<Object> argList = new ArrayList<>(); |
| | | Parameter[] parameters = method.getParameters(); |
| | | for (int i = 0; i < parameters.length; i++) { |
| | | //将RequestBody注解修饰的参数作为请求参数 |
| | | RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class); |
| | | if (requestBody != null) { |
| | | argList.add(args[i]); |
| | | } |
| | | // 是否需要保存response,参数和值 |
| | | if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) |
| | | { |
| | | operLog.setJsonResult(StringUtils.sub(JSON.toJSONString(jsonResult), 0, 2000)); |
| | | //将RequestParam注解修饰的参数作为请求参数 |
| | | RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class); |
| | | if (requestParam != null) { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | String key = parameters[i].getName(); |
| | | if (StringUtils.isNotEmpty(requestParam.value())) { |
| | | key = requestParam.value(); |
| | | } |
| | | map.put(key, args[i]); |
| | | argList.add(map); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取请求的参数,放到log中 |
| | | * |
| | | * @param operLog 操作日志 |
| | | * @throws Exception 异常 |
| | | */ |
| | | private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception |
| | | { |
| | | Map<?, ?> paramsMap = getParamMap(ServletUtils.getRequest()); |
| | | String requestMethod = operLog.getRequestMethod(); |
| | | if (ObjectUtil.isEmpty(paramsMap) |
| | | && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) |
| | | { |
| | | String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); |
| | | operLog.setOperParam(StringUtils.sub(params, 0, 2000)); |
| | | if (argList.size() == 0) { |
| | | return null; |
| | | } else if (argList.size() == 1) { |
| | | return argList.get(0); |
| | | } else { |
| | | return argList; |
| | | } |
| | | else{ |
| | | operLog.setOperParam(StringUtils.sub(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获得所有请求参数 |
| | | * |
| | | * @param request 请求对象{@link ServletRequest} |
| | | * @return Map |
| | | */ |
| | | private Map<String, String> getParamMap(ServletRequest request) |
| | | { |
| | | Map<String, String> params = new HashMap<>(); |
| | | Map<String, String[]> map = request.getParameterMap(); |
| | | for (Map.Entry<String, String[]> entry : Collections.unmodifiableMap(map).entrySet()) |
| | | { |
| | | params.put(entry.getKey(), StringUtils.join(",",entry.getValue())); |
| | | } |
| | | return params; |
| | | } |
| | | |
| | | /** |
| | | * 参数拼装 |
| | | */ |
| | | private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) |
| | | { |
| | | String params = ""; |
| | | if (paramsArray != null && paramsArray.length > 0) |
| | | { |
| | | for (Object o : paramsArray) |
| | | { |
| | | if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) |
| | | { |
| | | try |
| | | { |
| | | String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); |
| | | params += jsonObj.toString() + " "; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return params.trim(); |
| | | } |
| | | |
| | | /** |
| | | * 忽略敏感属性 |
| | | */ |
| | | public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) |
| | | { |
| | | return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); |
| | | } |
| | | |
| | | /** |
| | | * 判断是否需要过滤的对象。 |
| | | * |
| | | * @param o 对象信息。 |
| | | * @return 如果是需要过滤的对象,则返回true;否则返回false。 |
| | | */ |
| | | @SuppressWarnings("rawtypes") |
| | | public boolean isFilterObject(final Object o) |
| | | { |
| | | Class<?> clazz = o.getClass(); |
| | | if (clazz.isArray()) |
| | | { |
| | | return clazz.getComponentType().isAssignableFrom(MultipartFile.class); |
| | | } |
| | | else if (Collection.class.isAssignableFrom(clazz)) |
| | | { |
| | | Collection collection = (Collection) o; |
| | | for (Object value : collection) |
| | | { |
| | | return value instanceof MultipartFile; |
| | | } |
| | | } |
| | | else if (Map.class.isAssignableFrom(clazz)) |
| | | { |
| | | Map map = (Map) o; |
| | | for (Object value : map.entrySet()) |
| | | { |
| | | Map.Entry entry = (Map.Entry) value; |
| | | return entry.getValue() instanceof MultipartFile; |
| | | } |
| | | } |
| | | return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse |
| | | || o instanceof BindingResult; |
| | | } |
| | | |
| | | |