使用aop切面实现系统操作日志管理
一、思路:
新增表SYS_OPERATE_LOG(表结构在下面),在需要的地方加上@MyLog(value = "**"),value为表字段OPERATION的值.
表示发生了什么操作.比如:加@MyLog(value = "**")注解的地方就会进入切面SysLogAspect(代码如下),而这些操作具体的参数和时间,操作者,IP,都会被记录下来
步骤如下:
用到两个工具类:
1.IpUtils 获取IP地址
2.HttpContextUtils 获取HttpServletRequest对象
1.SYS_OPERATE_LOG建表语句:
-- sys日志操作表
CREATE TABLE SYS_OPERATE_LOG
(
KID VARCHAR2(32) NOT NULL,
USER_NAME VARCHAR2(32),
OPERATION VARCHAR2(32),
METHOD VARCHAR2(1024),
PARAMS VARCHAR2(1024),
IP VARCHAR2(32),
CREATE_TIME TIMESTAMP
);
alter table SYS_OPERATE_LOG
add constraint PK_SYS_OPERATE_LOG primary key (KID);
comment on table SYS_OPERATE_LOG
is 'sys日志操作表';
comment on column SYS_OPERATE_LOG.KID
is '主键';
comment on column SYS_OPERATE_LOG.USER_NAME
is '操作员姓名';
comment on column SYS_OPERATE_LOG.OPERATION
is '操作如( 新增操作员)';
comment on column SYS_OPERATE_LOG.METHOD
is '操作的方法com.test.****';
comment on column SYS_OPERATE_LOG.PARAMS
is '操作的参数';
comment on column SYS_OPERATE_LOG.IP
is 'ip地址';
comment on column SYS_OPERATE_LOG.CREATE_TIME
is '操作时间';
2.自定义注解@MyLog
import java.lang.annotation.*;
/**
* 自定义注解类
*/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //生成文档
public @interface MyLog {
String value() default "";
}
3.SysLogAspect切面
package ****.common.base;
import com.alibaba.fastjson.JSON;
import ****.common.util.HttpContextUtils;
import ****.common.util.IpUtils;
import ****.constant.UUIDGenerator;
import ****.dao.pojo.SysLog;
import ****.service.operate.ISysLogService;
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.security.core.context.SecurityContext;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 系统日志:切面处理类
*
*/
@Aspect
@Component
public class SysLogAspect {
@Autowired
private ISysLogService sysLogService;
//定义切点 @Pointcut
//在注解的位置切入代码
@Pointcut("@annotation( ****.common.base.MyLog)")
public void logPoinCut() {
}
//切面 配置通知
@AfterReturning("logPoinCut()")
public void saveSysLog(JoinPoint joinPoint) {
//保存日志
SysLog sysLog = new SysLog();
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
sysLog.setKid(UUIDGenerator.idGenerator());
//获取操作
MyLog myLog = method.getAnnotation(MyLog.class);
if (myLog != null) {
String value = myLog.value();
sysLog.setOperation(value);//保存获取的操作
}
//获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
//获取请求的方法名
String methodName = method.getName();
sysLog.setMethod(className + "." + methodName);
//请求的参数
Object[] args = joinPoint.getArgs();
List
//去除Authentication对象,或者不需要的参数,比如HttpServletRequest,这些对象的太大,存下来没什么卵用
//注意:要注意的是Authentication这个对象是单例的,因此在操作这个对象时会报反射错误.通过joinPoint拿到的对象实际上是克隆出来的
//并不是在上下文中的那个Authentication,所以无法转换.
//解决方法:新建一个argsList2将不要的对象排除掉
for (Object param:argsList) {
if(!param.getClass().getName().contains("Authentication")){
argsList2.add(param);
}
}
//将参数所在的数组转换成json
String params = JSON.toJSONString(argsList2);
sysLog.setParams(params);
//获取用户ip地址
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//通过request获取用户名
SecurityContext securityContext = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
UserDetails userDetails = (UserDetails) securityContext.getAuthentication().getPrincipal();
sysLog.setUserName(userDetails.getUsername());
sysLog.setIp(IpUtils.getIpAddr(request));
//调用service保存SysLog实体类到数据库
sysLogService.save(sysLog);
}
}
4. IpUtils和HttpContextUtils
(1).IpUtils
package ****.common.util;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpUtils {
private IpUtils() {
}
/**
* 获取当前网络ip
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request){
String ipAddress = request.getHeader("x-forwarded-for");
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}
return ipAddress;
}
/**
* 获得MAC地址
* @param ip
* @return
*/
public static String getMACAddress(String ip){
String str = "";
String macAddress = "";
try {
Process p = Runtime.getRuntime().exec("nbtstat -A " + ip);
InputStreamReader ir = new InputStreamReader(p.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
for (int i = 1; i < 100; i++) {
str = input.readLine();
if (str != null) {
if (str.indexOf("MAC Address") > 1) {
macAddress = str.substring(str.indexOf("MAC Address") + 14, str.length());
break;
}
}
}
} catch (IOException e) {
e.printStackTrace(System.out);
}
return macAddress;
}
}
(2).HttpContextUtils
package ****.common.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}