数据后台管理(五)AOP日志
为了增加数据的安全性,在数据管理的过程中,我们需要将操作者访问时间,操作者的名称,访问的IP,访问资源的URL,执行时长,访问方法记录下来存储到数据库中,并可以通过页面查看。
1.将日志信息存储到数据库中
1.1根据需要记录的日志内容在数据库中创建表syslog和对应的实体类SysLog
日志表syslog
SysLog类
1 package club.nipengfei.ssm.domain; 2 3 import java.util.Date; 4 5 public class SysLog { 6 private String id; 7 private Date visitTime; 8 private String visitTimeStr; 9 private String username; 10 private String ip; 11 private String url; 12 private Long executionTime; 13 private String method; 14 15 public String getId() { 16 return id; 17 } 18 19 public void setId(String id) { 20 this.id = id; 21 } 22 23 public Date getVisitTime() { 24 return visitTime; 25 } 26 27 public void setVisitTime(Date visitTime) { 28 this.visitTime = visitTime; 29 } 30 31 public String getVisitTimeStr() { 32 return visitTimeStr; 33 } 34 35 public void setVisitTimeStr(String visitTimeStr) { 36 this.visitTimeStr = visitTimeStr; 37 } 38 39 public String getUsername() { 40 return username; 41 } 42 43 public void setUsername(String username) { 44 this.username = username; 45 } 46 47 public String getIp() { 48 return ip; 49 } 50 51 public void setIp(String ip) { 52 this.ip = ip; 53 } 54 55 public String getUrl() { 56 return url; 57 } 58 59 public void setUrl(String url) { 60 this.url = url; 61 } 62 63 public Long getExecutionTime() { 64 return executionTime; 65 } 66 67 public void setExecutionTime(Long executionTime) { 68 this.executionTime = executionTime; 69 } 70 71 public String getMethod() { 72 return method; 73 } 74 75 public void setMethod(String method) { 76 this.method = method; 77 } 78 }
1.2在controller包下新建一个切面类LogAop来获取需要记录日志内容
注意该类作为切面类需要注解@Aspect
在该切面类内创建一个前置通知@Before("execution(* club.nipengfei.ssm.controller.*.*(..))"),一个后置通知@After("execution(* club.nipengfei.ssm.controller.*.*(..))"),注解内的属性表示切入点表达式,在这里表示controller包下的所有类。
1.2.1获取操作者的访问时间
直接在前置通知内new一个Date()
1.2.2获取操作者名称
1 // 获取当前操作的用户 2 SecurityContext context = SecurityContextHolder.getContext(); 3 User user =(User) context.getAuthentication().getPrincipal(); 4 String username = user.getUsername();
1.2.3获取访问IP
先在web.xml中新增一个监听器
1 <listener> 2 <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> 3 </listener>
通过getRemoteAddr()方法获取IP
1 // 获取IP地址 2 String ip = request.getRemoteAddr();
1.2.4获取访问资源的URL
思路:获取类上注解的@RequestMapping的属性值和方法上注解@RequestMapping的属性值,并将两者拼接。
获取类上注解属性值:通过反射获取操作的类,使用getAnnotation(RequestMapping.class)方法获取@RequestMapping注解,使用value()获取属性
获取方法上注解属性值:通过反射获取操作的类,使用getMethod()方法获取方法,使用getAnnotation(RequestMapping.class)方法获取@RequestMapping注解,使用value()获取属性
具体代码放下面。
1.2.5获取执行时长
在后置通知内new一个Date()减去前置通知的Date()。
1 // 获取访问时长 2 long time = new Date().getTime()-visitTime.getTime();
1.2.6获取访问方法
通过类的getMethod()方法获取方法。
注意:有些方法含参,有些不含参需要分开处理。
1 @Before("execution(* club.nipengfei.ssm.controller.*.*(..))") 2 public void doBefore(JoinPoint jp) throws NoSuchMethodException { 3 visitTime = new Date(); // 当前时间就是开始访问的类 4 clazz = jp.getTarget().getClass(); // 具体访问的类 5 String methodName = jp.getSignature().getName(); // 获取访问方法名称 6 Object[] args = jp.getArgs(); // 获取访问方法参数 7 8 // 获取具体执行方法Method对象 9 if (args==null || args.length==0){ 10 method = clazz.getMethod(methodName); 11 } else { 12 Class[] classArgs = new Class[args.length]; 13 for (int i=0;i<args.length;i++){ 14 classArgs[i] = args[i].getClass(); 15 } 16 method = clazz.getMethod(methodName,classArgs); 17 } 18 }
LogAop类的代码:
1 package club.nipengfei.ssm.controller; 2 3 import club.nipengfei.ssm.domain.SysLog; 4 import club.nipengfei.ssm.service.ISysLogService; 5 import org.aspectj.lang.JoinPoint; 6 import org.aspectj.lang.annotation.After; 7 import org.aspectj.lang.annotation.Aspect; 8 import org.aspectj.lang.annotation.Before; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.security.core.context.SecurityContext; 11 import org.springframework.security.core.context.SecurityContextHolder; 12 import org.springframework.security.core.userdetails.User; 13 import org.springframework.stereotype.Component; 14 import org.springframework.web.bind.annotation.RequestMapping; 15 16 import javax.servlet.http.HttpServletRequest; 17 import java.lang.reflect.Method; 18 import java.util.Date; 19 20 @Component 21 @Aspect 22 public class LogAop { 23 24 @Autowired 25 private HttpServletRequest request; 26 27 @Autowired 28 private ISysLogService sysLogService; 29 30 private Date visitTime; // 开始时间 31 private Class clazz; // 访问的类 32 private Method method; // 访问的方法 33 34 // 前置通知 主要获取开始时间,执行的类哪一个,执行的哪一个方法 35 @Before("execution(* club.nipengfei.ssm.controller.*.*(..))") 36 public void doBefore(JoinPoint jp) throws NoSuchMethodException { 37 visitTime = new Date(); // 当前时间就是开始访问的类 38 clazz = jp.getTarget().getClass(); // 具体访问的类 39 String methodName = jp.getSignature().getName(); // 获取访问方法名称 40 Object[] args = jp.getArgs(); // 获取访问方法参数 41 42 // 获取具体执行方法Method对象 43 if (args==null || args.length==0){ 44 method = clazz.getMethod(methodName); 45 } else { 46 Class[] classArgs = new Class[args.length]; 47 for (int i=0;i<args.length;i++){ 48 classArgs[i] = args[i].getClass(); 49 } 50 method = clazz.getMethod(methodName,classArgs); 51 } 52 } 53 54 // 后置通知 55 @After("execution(* club.nipengfei.ssm.controller.*.*(..))") 56 public void doAfter(JoinPoint jp) throws Exception { 57 58 // 获取访问时长 59 long time = new Date().getTime()-visitTime.getTime(); 60 61 String url = ""; 62 // 获取url 63 if (clazz != null && method !=null && clazz!=LogAop.class){ 64 // 获取类的@RequestMapping("/orders") 65 RequestMapping clazzAnnotation =(RequestMapping) clazz.getAnnotation(RequestMapping.class); 66 if (clazzAnnotation != null){ 67 String[] classValue = clazzAnnotation.value(); 68 // 获取方法上的@RequestMapping("xxx") 69 RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class); 70 if (methodAnnotation != null){ 71 String[] methodValue = methodAnnotation.value(); 72 url=classValue[0]+methodValue[0]; 73 74 // 获取IP地址 75 String ip = request.getRemoteAddr(); 76 77 // 获取当前操作的用户 78 SecurityContext context = SecurityContextHolder.getContext(); 79 User user =(User) context.getAuthentication().getPrincipal(); 80 String username = user.getUsername(); 81 82 // 将日志相关信息封装到SysLog对象 83 SysLog sysLog = new SysLog(); 84 sysLog.setExecutionTime(time); 85 sysLog.setIp(ip); 86 sysLog.setMethod("[类名] "+clazz.getName()+"[方法名] "+method.getName()); 87 sysLog.setUrl(url); 88 sysLog.setUsername(username); 89 sysLog.setVisitTime(visitTime); 90 91 // 调用service完成操作 92 sysLogService.save(sysLog); 93 } 94 } 95 96 } 97 } 98 }
1.3在service.impl包下新建一个SysLogServiceImpl类,生成一个save方法将SysLog类对象放到数据表中
1 package club.nipengfei.ssm.service.impl; 2 3 import club.nipengfei.ssm.dao.ISysLogDao; 4 import club.nipengfei.ssm.domain.SysLog; 5 import club.nipengfei.ssm.service.ISysLogService; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.stereotype.Service; 8 import org.springframework.transaction.annotation.Transactional; 9 10 import java.util.List; 11 12 @Service 13 @Transactional 14 public class SysLogServiceImpl implements ISysLogService { 15 16 @Autowired 17 private ISysLogDao sysLogDao; 18 19 @Override 20 public void save(SysLog sysLog) throws Exception { 21 sysLogDao.save(sysLog); 22 } 23 24 }
1.4在dao包下新建ISysLogDao接口
1 package club.nipengfei.ssm.dao; 2 3 import club.nipengfei.ssm.domain.SysLog; 4 import org.apache.ibatis.annotations.Insert; 5 import org.apache.ibatis.annotations.Select; 6 7 import java.util.List; 8 9 public interface ISysLogDao { 10 11 @Insert("insert into syslog(visitTime,username,ip,url,executionTime,method) values(#{visitTime},#{username},#{ip},#{url},#{executionTime},#{method})") 12 public void save(SysLog sysLog) throws Exception; 13 14 }
1.5存在的问题
当点击订单管理时发现,不能正常访问
查看资料发现因为该findAll方法传入的形参是int类型,而我们的切面类通过反射获取该类的方法时传入的参数Integer类型。将findAll方法的int改为Integer,发现能正常访问了。
2.将日志信息展示到页面上
流程分析图:
2.1在ISysLogDao接口中生成一个findAll方法
1 package club.nipengfei.ssm.dao; 2 3 import club.nipengfei.ssm.domain.SysLog; 4 import org.apache.ibatis.annotations.Insert; 5 import org.apache.ibatis.annotations.Select; 6 7 import java.util.List; 8 9 public interface ISysLogDao { 10 11 @Insert("insert into syslog(visitTime,username,ip,url,executionTime,method) values(#{visitTime},#{username},#{ip},#{url},#{executionTime},#{method})") 12 public void save(SysLog sysLog) throws Exception; 13 14 @Select("select * from sysLog") 15 List<SysLog> findAll() throws Exception; 16 }
2.2在SysLogServiceImpl类内调用上面方法
1 package club.nipengfei.ssm.service.impl; 2 3 import club.nipengfei.ssm.dao.ISysLogDao; 4 import club.nipengfei.ssm.domain.SysLog; 5 import club.nipengfei.ssm.service.ISysLogService; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.stereotype.Service; 8 import org.springframework.transaction.annotation.Transactional; 9 10 import java.util.List; 11 12 @Service 13 @Transactional 14 public class SysLogServiceImpl implements ISysLogService { 15 16 @Autowired 17 private ISysLogDao sysLogDao; 18 19 @Override 20 public void save(SysLog sysLog) throws Exception { 21 sysLogDao.save(sysLog); 22 } 23 24 @Override 25 public List<SysLog> findAll() throws Exception { 26 return sysLogDao.findAll(); 27 } 28 }
2.3在controller包下新建一个SysLogController类
1 package club.nipengfei.ssm.controller; 2 3 import club.nipengfei.ssm.domain.SysLog; 4 import club.nipengfei.ssm.service.ISysLogService; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.stereotype.Controller; 7 import org.springframework.web.bind.annotation.RequestMapping; 8 import org.springframework.web.servlet.ModelAndView; 9 10 import java.util.List; 11 12 @Controller 13 @RequestMapping("/sysLog") 14 public class SysLogController { 15 16 @Autowired 17 private ISysLogService sysLogService; 18 19 @RequestMapping("/findAll.do") 20 public ModelAndView findAll() throws Exception { 21 ModelAndView mv = new ModelAndView(); 22 List<SysLog> sysLogList = sysLogService.findAll(); 23 mv.addObject("sysLogs",sysLogList); 24 mv.setViewName("syslog-list"); 25 return mv; 26 } 27 }