Spring Boot利用AOP获取用户操作实现日志记录

本文介绍Spring Boot利用AOP获取用户操作实现日志记录

环境:IDEA版本2017.3.1 x64, JDK1.8, SpringBoot2.1.1, Druid1.1.8, mybatis1.3.2,Security5.1.2,thymeleaf3.0.11

思路总结:首先在需要做日志记录的方法中添加一个自定义注解,再去实现一个日志AOP类,AOP类把自定义注解设置为切点,所以当系统执行某一个添加了自定义注解的方法时,AOP会自动获取该方法名称以及用户信息实现日志记录。

编写自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 自定义注解类 定义controller方法的中文含义
* @Target({METHOD,TYPE}) 表示这个注解可以用用在类/接口上,还可以用在方法上
* @Retention(RetentionPolicy.RUNTIME) 表示这是一个运行时注解,即运行起来之后,才获取注解中的相关信息,而不像基本注解如@Override 那种不用运行,在编译时eclipse就可以进行相关工作的编译时注解。
* @Inherited 表示这个注解可以被子类继承
* @Documented 表示当执行javadoc的时候,本注解会生成相关文档
*/

@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Operation {
String value() default "";
}

日志AOP类会用到获取IP地址的工具类,也用到json工具类,实现工具类如下

实现必要工具类

获取ip地址工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
        /**
* 获取用户真实的ip地址
* @param request
* @return
*/
public class IpAdrressUtil {

public static String getIpAdrress(HttpServletRequest request) {
String ip = null;

//X-Forwarded-For:Squid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
String unknown = "unknown";
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IP:apache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}

if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IP:weblogic 服务代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}

if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//HTTP_CLIENT_IP:有些代理服务器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}

if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//X-Real-IP:nginx服务代理
ipAddresses = request.getHeader("X-Real-IP");
}

//有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}

//还是不能获取到,最后再通过request.getRemoteAddr();获取
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}


}
实现json工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class JacksonUtil {

private final static ObjectMapper objectMapper = new ObjectMapper();

private JacksonUtil() {

}

public static ObjectMapper getInstance() {
return objectMapper;
}

/**
* javaBean、列表数组转换为json字符串
*/
public static String obj2json(Object obj) throws Exception {
return objectMapper.writeValueAsString(obj);
}

/**
* json 转JavaBean
*/

public static <T> T json2pojo(String jsonString, Class<T> clazz) throws Exception {
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
return objectMapper.readValue(jsonString, clazz);
}

/**
* json字符串转换为map
*/
public static <T> Map<String, Object> json2map(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.readValue(jsonString, Map.class);
}
}

实现日志AOP类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* 系统日志:切面处理类
*/
@Aspect
@Component
public class SysLogAspect {

@Autowired
private SysLogService sysLogService;

//定义切点 @Pointcut
//在注解的位置切入代码
@Pointcut("@annotation(cn.springboot.util.Operation)")
public void logPoinCut() {
}

//切面 配置通知
@AfterReturning("logPoinCut()")
public void saveSysLog(JoinPoint joinPoint) {
//保存日志
SysLog sysLog = new SysLog();

//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();


//获取操作
Operation operation = method.getAnnotation(Operation.class);
if (operation != null) {
String value = operation.value();
sysLog.setOperation(value);//保存获取的操作
}

//获取请求的类名
String className = joinPoint.getTarget().getClass().getName();

//获取请求的方法名
String methodName = method.getName();
sysLog.setMethod(className + "." + methodName);

//请求的参数
Object[] args = joinPoint.getArgs();
//将参数所在的数组转换成json
String params = null;
try {
params = JacksonUtil.obj2json(args);
} catch (Exception e) {
e.printStackTrace();
}
sysLog.setParams(params);

//请求的时间
sysLog.setCreateDate(new Date());

//获取用户名
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof AnonymousAuthenticationToken)) {
sysLog.setUsername(authentication.getName());
}

//获取用户ip地址
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
sysLog.setIp(IpAdrressUtil.getIpAdrress(request));

//调用service保存SysLog实体类到数据库
sysLogService.saveLog(sysLog);
}

}

最后,我是调用service保存SysLog实体类到数据库,你也可以直接输出到控制台,要保存到数据库,还要实现service类,mapper类和javeBean。
我就简单贴个javaBean,其他类就不具体贴出了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysLog implements Serializable {
private Long id;

private String username; //用户名

private String operation; //操作

private String method; //方法名

private String params; //参数

private String ip; //ip地址

private Date createDate; //操作时间
//创建getter和setter方法
}

javaBean中使用lombok的注解实现了get、set等方法。

以下是存进数据库的数据,关于IP的问题是因为本地测试,部署在服务器上就会有正常的IP地址了。

如果我的文章对你有很大帮助 那么不妨?
-------------本文结束感谢您的阅读-------------
0%