資料後臺管理(五)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 }
&n