在安全审计方面或者日志分析中,很多时候都要记录用户的操作行为,特别是用户登录和特殊模块的操作。下面学习日志统一处理。将使用AOP、采用注解切点的方式做个横向的操作日志写入数据库操作。
定义AOP日志处理类,将自定义注解作为切点,目标方法执行后,构造日志,写入数据库。
@Aspect
@Component
public class LogAopComponent {
private static final Logger logger = LoggerFactory.getLogger(LogAopComponent.class);
@Autowired
ExceptionLogExecutors executors;
/**
* 注解切点
*
* @author wenqy
*/
@Pointcut(“@annotation(com.wenqy.log.OperateLog)”)
private void pointCutMethod(){}
/**
* 基于切点
* @param joinPoint
* @author wenqy
* @throws ClassNotFoundException
*/
@After(“pointCutMethod()”)
public void recordOperateLog(JoinPoint joinPoint) throws ClassNotFoundException{
OperateLogVO operateLog = createOperateLog(joinPoint);
// 扔到线程池执行
this.executors.execute(new OperateLogThread(operateLog ));
}
/**
* 获取操作日志
* @param joinPoint
* @return
* @throws ClassNotFoundException
* @author wenqy
*/
public OperateLogVO createOperateLog(JoinPoint joinPoint) throws ClassNotFoundException {
OperateLogVO operateLog = new OperateLogVO();
String clientIp = WebUtils.getClientIp();
operateLog.setHostIp(clientIp);
// TODO 取session
operateLog.setUserId(IdGenerator.randomLong());
operateLog.setOperateTime(new Date());
// 获取操作方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法类名
String targetName = joinPoint.getTarget().getClass().getName();
Class<?> targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
for (Method method : methods){
if(!method.getName().equals(methodName)){
continue;
}
// 获取方法注解
OperateLog logAnnotation = method.getAnnotation(OperateLog.class);
operateLog.setAction(logAnnotation.action());
operateLog.setModule(logAnnotation.module());
operateLog.setRemark(logAnnotation.remark());
operateLog.setSubSystem(logAnnotation.subSystem());
break;
}
return operateLog;
}
}
自定义切点注解,描述日志信息。
/**
* 日志注解
*
* @version V5.0
* @author wenqy
* @date 2018年2月14日
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) // 目标方法
@Documented
public @interface OperateLog {
/**
* 子系统
* @return
* @author wenqy
*/
String subSystem() default “”;
/**
* 模块
* @return
* @author wenqy
*/
String module() default “”;
/**
* 动作
* @return
* @author wenqy
*/
String action() default “”;
/**
* 备注
* @return
* @author wenqy
*/
String remark() default “”;
}
根据注解信息和用户信息,定义操作日志VO类
public class OperateLogVO implements Serializable {
private Long id;
private String subSystem;
private String module;
private String action;
private String remark;
private String hostIp; // ip
private Date operateTime; // 操作时间
private Long userId; // 操作用户Id
// 忽略setter getter
}
定义保存操作日志线程
/**
* 操作日志保存线程
*
* @version V5.0
* @author wenqy
* @date 2018年2月14日
*/
public class OperateLogThread implements Runnable {
private OperateLogVO operateLogVO;
public OperateLogThread(OperateLogVO operateLogVO) {
this.operateLogVO = operateLogVO;
}
@Override
public void run() {
// 加载异常日志处理服务
OperateLogService logService = (OperateLogService) SpringAppContextHolder
.getBean(OperateLogService.class);
logService.saveOperateLog(operateLogVO);
}
}
控制层中要记录操作日志的方法定义引用
@OperateLog(subSystem=“系统管理”, module=“系统登录”, action=“login”,remark=“用户登录”)
@RequestMapping(value = “/doLogin”)
@ResponseBody
public Object doLogin(HttpServletRequest request) {
String email = request.getParameter(“email”);
String password = request.getParameter(“password”);
System.out.println(email + ” “ + password);
return “success”;
}
启动应用,访问/doLogin URL链接,查看是否是否插库
使用的线程池为上篇spring boot学习系列之统一异常处理6中定义的线程池,保存操作日志的Service和Mybatis Mapper也与上篇异常日志保存类似,代码就不贴了。也有采用父类定义操作日志方法或者利用拦截器的写法,这些都可以实现。
本文由 wenqy 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Nov 8,2020