基于注解的Aop日志记录
项目安全送检前需要做个审计日志,做完后在这记录下实现过程
1.Log实体类
package com.ideal.manage.guest.bean.log; import javax.persistence.*; import java.io.Serializable; import java.util.Date; /** * @apiNote 日志 * @author lcoil * @since 2020-05-24 */ @Data public class Log { private Long id; private String content; private String description; private String ip; private String module; private String username; private Date createAt; private Date updateAt; private Integer able; }
2.定义注解
package com.ideal.manage.guest.annotation; import java.lang.annotation.*; /** * @author lcoil * @create 2020-05-24 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /**模块*/ String module() default ""; /**描述*/ String description() default ""; }
3.定义切面
package com.ideal.manage.guest.aop; import com.ideal.manage.guest.annotation.Log; import com.ideal.manage.guest.service.log.LogService; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 日志切面处理类 * * @author lcoil * @create 2020-05-24 */ @Aspect @Component public class LogAspect { @Autowired private LogService logService; /** * 日志切入点 */ @Pointcut("@annotation(com.ideal.manage.guest.annotation.Log)") public void logPointCut(){} /** * 前置通知 * @param joinPoint */ @Before(value ="execute()") public void Before(JoinPoint joinPoint) { /** * 解析Log注解 */ String methodName = joinPoint.getSignature().getName(); Method method = currentMethod(joinPoint,methodName); Log log = method.getAnnotation(Log.class); logService.put(joinPoint,methodName,log.module(),log.description()); } /** * 环绕通知 * @param proceedingJoinPoint * @return */ @Around(value ="execute()") public Object around(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("环绕通知开始"); try { System.out.println("执行方法:" + proceedingJoinPoint.getSignature().getName()); MethodSignature signature =(MethodSignature) proceedingJoinPoint.getSignature(); Action action = signature.getMethod().getAnnotation(Action.class); System.out.println("菜单="+action.description()); Object object = proceedingJoinPoint.proceed(); System.out.println("环绕通知结束,方法返回:" + object); return object; } catch (Throwable e) { System.out.println("执行方法异常:" + e.getClass().getName()); return null; } } /** * 后置通知 * @param joinPoint */ @After(value ="execute()") public void After(JoinPoint joinPoint) { System.out.println("执行方法之后"); } /** * 后置通知,带返回值 * @param obj */ @AfterReturning(pointcut = "execute()",returning = "obj") public void doAfter(Object obj){ System.out.println("执行方法之后获取返回值:"+obj); } /** * 后置通知,异常时执行 * @param e */ @AfterThrowing(throwing = "e",pointcut = "execute()") public void doAfterThrowing(Exception e) { System.out.println("执行方法异常:"+e.getClass().getName()); } /** * 获取当前执行的方法 * * @param joinPoint 连接点 * @param methodName 方法名称 * @return 方法 */ private Method currentMethod(JoinPoint joinPoint, String methodName) { /** * 获取目标类的所有方法,找到当前要执行的方法 */ Method[] methods = joinPoint.getTarget().getClass().getMethods(); Method resultMethod = null; for (Method method : methods) { if (method.getName().equals(methodName)) { resultMethod = method; break; } } return resultMethod; } }
4.业务处理:LogService
package com.ideal.manage.guest.service.log; import com.alibaba.fastjson.JSONObject; import com.ideal.manage.guest.bean.DTO.PageDto; import com.ideal.manage.guest.bean.log.Log; import com.ideal.manage.guest.config.shiro.MyShiroRealm; import com.ideal.manage.guest.repository.framework.MySpecification; import com.ideal.manage.guest.repository.framework.SpecificationOperator; import com.ideal.manage.guest.repository.log.LogRepository; import com.ideal.manage.guest.util.Const; import com.ideal.manage.guest.util.HttpRequests; import com.ideal.manage.guest.util.IPUtils; /*import org.apache.ibatis.javassist.*; import org.apache.ibatis.javassist.bytecode.CodeAttribute; import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute; import org.apache.ibatis.javassist.bytecode.MethodInfo;*/ import javassist.*; import javassist.bytecode.CodeAttribute; import javassist.bytecode.LocalVariableAttribute; import javassist.bytecode.MethodInfo; import org.apache.shiro.SecurityUtils; import org.aspectj.lang.JoinPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * 服务实现类 * * @author lcoil * @since 2020-05-24 */ @Service public class LogService{ private static final String LOG_CONTENT = "[类名]:%s,[方法]:%s,[参数]:%s,[IP]:%s"; private String username; @Autowired private LogRepository logRepository; public String initUsername(String username) { if(!StringUtils.isEmpty(username)){ this.username = username; } return this.username; } public void put(JoinPoint joinPoint, String methodName, String module, String description) { try { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); Log log = new Log(); //username = ((JwtClient) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername(); // 获取当前登录用户 MyShiroRealm.ShiroUser shiroUser = (MyShiroRealm.ShiroUser) SecurityUtils.getSubject().getPrincipal(); username = shiroUser.getUsername(); if (StringUtils.isEmpty(username)) { username = Const.USERNAME_IN_CONTEXT != null ? Const.USERNAME_IN_CONTEXT : "未知用户"; } String ip = IPUtils.getIpAddr(request); log.setUsername(username); log.setModule(module); log.setDescription(description); log.setIp(ip); log.setContent(operateContent(joinPoint, methodName, ip, request)); log.setAble(1); logRepository.save(log); } catch (Exception e) { e.printStackTrace(); } } /*public Page<Log> page(LogRequest request, Page<Log> page) { if(request == null){ request = new LogRequest(); } request.setIsDeleted(Config.ABLE_CONFIG.ABLE); List<Log> logs = baseMapper.page(request,page); page.setRecords(logs); return page; }*/ /** * 查询所有日志 * @param pageNum * @param request * @return */ public PageDto findAll(int pageNum, HttpServletRequest request) { Sort sort = new Sort(Sort.Direction.DESC, "updateAt"); List<SpecificationOperator> operators = HttpRequests.getParametersStartingWith(request, "Q_"); //增加删除标识的过滤 SpecificationOperator isValid = new SpecificationOperator("able", "1", "EQ"); operators.add(isValid); MySpecification<Log> mySpecifications = new MySpecification<>(operators); Pageable pageable = new PageRequest(pageNum, 10, sort); Page<Log> page = logRepository.findAll(mySpecifications, pageable); //设置PageDto List<Log> parameters = page.getContent(); long total = page.getTotalElements(); PageDto pageDto = new PageDto(); pageDto.setRows(parameters); pageDto.setTotal(total); return pageDto; } public String operateContent(JoinPoint joinPoint, String methodName, String ip, HttpServletRequest request) throws ClassNotFoundException, NotFoundException { String className = joinPoint.getTarget().getClass().getName(); Object[] params = joinPoint.getArgs(); String classType = joinPoint.getTarget().getClass().getName(); Class<?> clazz = Class.forName(classType); String clazzName = clazz.getName(); Map<String,Object > nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName,params); StringBuffer bf = new StringBuffer(); if (!CollectionUtils.isEmpty(nameAndArgs)){ Iterator it = nameAndArgs.entrySet().iterator(); while (it.hasNext()){ Map.Entry entry = (Map.Entry) it.next(); String key = (String) entry.getKey(); String value = JSONObject.toJSONString(entry.getValue()); bf.append(key).append("="); bf.append(value).append("&"); } } if (StringUtils.isEmpty(bf.toString())){ bf.append(request.getQueryString()); } return String.format(LOG_CONTENT, className, methodName, bf.toString(), ip); } private Map<String,Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException { Map<String,Object > map=new HashMap<String,Object>(); ClassPool pool = ClassPool.getDefault(); ClassClassPath classPath = new ClassClassPath(cls); pool.insertClassPath(classPath); CtClass cc = pool.get(clazzName); CtMethod cm = cc.getDeclaredMethod(methodName); MethodInfo methodInfo = cm.getMethodInfo(); CodeAttribute codeAttribute = methodInfo.getCodeAttribute(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); if (attr == null) { return map; } int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1; for (int i = 0; i < cm.getParameterTypes().length; i++){ map.put( attr.variableName(i + pos),args[i]);//paramNames即参数名 } return map; } }
5.使用注解在需要的地方记录日志
@Log(module = "设备管理",description = "添加设备") public Integer saveEquipment(Equipment equipment) { equipment.setAble(1); Equipment resultEquipment = equipmentRepository.save(equipment); if(resultEquipment == null){ return StateUtil.RESULT_FAILED; } return StateUtil.RESULT_SUCCESS; }