使用AOP监控用户操作并插入数据库
引入依赖
<!--spring切面aop依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
在application.properties添加配置
spring.aop.auto=true
1.创建自定义注解类
当然该注解类可以使用spring自带注解类:@Before,@After,@AfterRuturning ,@AfterThrowing ,@Around 。
只是这5种方式的作用不同:
(1)Before ---在所拦截方法执行前执行;
(2)After ---在所拦截方法执行后执行;
(3)AfterRuturning ---在所拦截方法返回值后,执行;
(4)AfterThrowing ---当所拦截方法抛出异常时,执行;
(5)Around ---最为复杂的切入方式,刚方式可以包括上述4个方式。
import java.lang.annotation.*; /** * 自定义注解类 * @author MrRoot * @date 2019-01-16 */ @Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上 @Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行 @Documented //生成文档 public @interface MyLog { String value() default ""; }
2.生成日志实体类
博主使用的是mybatisplus根据表信息生成的实体类,当然也可以使用普通实体类。
import com.baomidou.mybatisplus.enums.IdType; import java.util.Date; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.enums.FieldFill; import com.baomidou.mybatisplus.activerecord.Model; import com.baomidou.mybatisplus.annotations.TableName; import java.io.Serializable; /** * <p> * 日志表 * </p> * * @author MrRoot * @since 2019-01-16 */ @TableName("research_log") public class Log extends Model<Log> { private static final long serialVersionUID = 1L; /** * 主键 */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 用户id */ @TableField("user_id") private Long userId; /** * 操作时用户的ip地址 */ private String ip; /** * 操作详情 */ private String operation; /** * 创建时间 */ @TableField(value = "gmt_create", fill = FieldFill.INSERT) private Date gmtCreate; /** * 更新时间 */ @TableField(value = "gmt_modified", fill = FieldFill.INSERT_UPDATE) private Date gmtModified; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getOperation() { return operation; } public void setOperation(String operation) { this.operation = operation; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModified() { return gmtModified; } public void setGmtModified(Date gmtModified) { this.gmtModified = gmtModified; } @Override protected Serializable pkVal() { return this.id; } @Override public String toString() { return "Log{" + ", id=" + id + ", userId=" + userId + ", ip=" + ip + ", operation=" + operation + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + "}"; } }
3.系统日志:处理切面类
其中最后的insert方法为mybatisplus封装好的插入方法,如果使用普通实体类则需要自己写插入数据库的方法。
IPUtils方法见附录
import com.alibaba.fastjson.JSON; import com.panshi.cecdc.research.entity.Log; import com.panshi.cecdc.research.service.LogService; import com.panshi.cecdc.research.util.IPUtils; import org.apache.shiro.SecurityUtils; 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.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Date; /** * 系统日志:切面处理类 * @author MrRoot * @date 2019-01-16 */ @Aspect @Component public class SysLogAspect { @Autowired private LogService logService; /** * 定义切点 @Pointcut * 在注解的位置切入代码 */ @Pointcut("@annotation(com.panshi.cecdc.research.common.MyLog)") public void logPointCut() { } /** * 切面 配置通知 * @param joinPoint */ @AfterReturning("logPointCut()")//这里可以选择日志插入的方式 public void saveSysLog(JoinPoint joinPoint) { //保存日志 Log log = new Log(); //从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //获取切入点所在的方法 Method method = signature.getMethod(); //获取操作 MyLog myLog = method.getAnnotation(MyLog.class); if (myLog != null) { String value = myLog.value(); //保存获取的操作 log.setOperation(value); } //获取请求的类名 //String className = joinPoint.getTarget().getClass().getName(); //获取请求的方法名 //String methodName = method.getName(); //sysLog.setMethod(className + "." + methodName); //请求的参数 //Object[] args = joinPoint.getArgs(); //将参数所在的数组转换成json //String params = JSON.toJSONString(args); //log.setParams(params); log.setGmtCreate(new Date()); //获取用户名 log.setUserId((Long)SecurityUtils.getSubject().getSession().getAttribute("userId")); //获取用户ip地址 HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); log.setIp(IPUtils.getIp(request)); //调用service保存SysLog实体类到数据库 logService.insert(log); } }
4.在需要监控的controller方法上加上注解
@MyLog(value = "用户详情")//操作类型 @GetMapping("/detail") public Result detail(@RequestParam("id") Long id){ return Result.createBySuccess(sysUserService.detail(id)); }
5.结果示例
附录:
IPUtils:
import org.apache.commons.lang3.StringUtils; import javax.servlet.http.HttpServletRequest; /** * ip工具类 * @author MrRoot * @date 2019-01-16 */ public class IPUtils { private static final String UNKNOWN = "unKnown"; public static String getIp(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if(StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)){ //多次反向代理后会有多个ip值,第一个ip才是真实ip int index = ip.indexOf(","); if(index != -1){ return ip.substring(0,index); }else{ return ip; } } ip = request.getHeader("X-Real-IP"); if(StringUtils.isNotEmpty(ip) && !UNKNOWN.equalsIgnoreCase(ip)){ return ip; } return request.getRemoteAddr(); } }