使用Spring AOP自定義註解方式實現使用者操作日誌記錄
阿新 • • 發佈:2018-11-02
1,開發環境
作業系統:Windows 7
JDK:1.8.0_161
Eclipse:Mars.2 Release (4.5.2)
2,自定義註解類UserLog
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface UserLog { /** 要執行的操作內容比如:使用者:XXX登入成功 **/ public String operationContent() default ""; }
3,日誌處理切面UserLogAspect
這裡使用的是後通知,當業務程式碼執行完成後,再執行日誌儲存。
@Aspect @Component public class UserLogAspect { @Autowired private ILogSysUserService logSysUserService; //Controller層切點 @Pointcut("@annotation(com.riskraider.common.annotation.UserLog)") public void controllerAspect() { } /** * 前置通知 用於攔截Controller層記錄使用者的操作 * * @param joinPoint 切點 */ @SuppressWarnings("rawtypes") @After("controllerAspect()") public void doAfter(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); //讀取session中的使用者 SysAccount account = (SysAccount) session.getAttribute("account"); if(null != account){ //獲取請求ip String ip = request.getRemoteAddr(); try{ String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String operationUrl = request.getServletPath(); String operationContent = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { //凡是加了UserLog註解的方法都記錄日誌 if(null != method.getAnnotation(UserLog.class)){ operationContent = method.getAnnotation(UserLog.class).operationContent(); //獲取使用者請求方法的引數並序列化為JSON格式字串 Map<String,Object> nameAndArgs = null; if(null != joinPoint.getArgs() && joinPoint.getArgs().length > 0) { //獲取引數名稱和值 nameAndArgs = getParameMap(joinPoint); } SysUserLogDTO log = new SysUserLogDTO(); log.setUserId(account.getAccountId()); log.setUserName(account.getName()); log.setUserIp(ip); log.setOperationUrl(operationUrl); log.setOperationContent(operationContent); log.setParameMap(nameAndArgs); logSysUserService.saveSysUserLog(log); break; } } } } }catch(Exception e) { LoggerUtils.error("記錄使用者操作日誌失敗!", e); } } } /** * @Title: getParameMap * @Description: 獲取引數的名稱和值 * @author: 施勇 * @date: 2018年10月3日 下午3:12:26 * @param: @param joinPoint * @param: @return * @return: Map<String,Object> * @throws */ public Map<String,Object> getParameMap(JoinPoint joinPoint){ Map<String, Object> map = new HashMap<String, Object>(); Object[] args = joinPoint.getArgs(); // 引數值 String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames(); // 引數名 for(int i=0;i<argNames.length;i++){ if("response".equals(argNames[i]) || "request".equals(argNames[i]) || "pwd".equals(argNames[i])){ continue; }else{ map.put(argNames[i], args[i]); } } return map; } }
getParameMap()這個方法是用於獲取使用者請求中的引數,只需要儲存業務相關的引數就可以了,所以將request和response過濾掉了,同時也不儲存使用者登入時的密碼。對這個引數Map在儲存到資料庫中的時候可以轉換成JSONString,這樣方便檢視。
4,使用註解
在需要記錄日誌的地方加上註解
@RequestMapping("/doLogin") @ResponseBody @UserLog(operationContent="登入系統") public ResultBean doLogin(String loginName, String pwd, HttpServletRequest request,HttpServletResponse response){ }
這裡重點是AOP切面儲存使用者操作日誌,其他AOP相關配置就不重複說了。