在SpringBoot集成Spring Security(1)——入门程序中添加日志功能。
原理:利用Spring Aop面向切面来获取信息。
步骤如下:
在原github上修改项目
1.添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.工具类
IpUtiles.java
package com.hjl.springsecurity.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 10:29
*/
public class JsonUtiles {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
* <p>Title: pojoToJson</p>
* <p>Description: </p>
* @param data
* @return
*/
public static String objectToJson(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData json数据
* @param beanType 对象中的object类型
* @return
*/
public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
* <p>Title: jsonToList</p>
* <p>Description: </p>
* @param jsonData
* @param beanType
* @return
*/
public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
JsonUtiles.java
package com.hjl.springsecurity.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 10:29
*/
public class JsonUtiles {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
* <p>Title: pojoToJson</p>
* <p>Description: </p>
* @param data
* @return
*/
public static String objectToJson(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData json数据
* @param beanType 对象中的object类型
* @return
*/
public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
* <p>Title: jsonToList</p>
* <p>Description: </p>
* @param jsonData
* @param beanType
* @return
*/
public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
3.新建日志实体类和基本的业务
SysLog.java实体类
package com.hjl.springsecurity.entity;
import java.io.Serializable;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 9:54
*/
public class SysLog implements Serializable {
private Long id;
private String uri;//请求接口
private String method;//请求方式
private String MethodDescribe;//描述
private String params;//参数
private String username; //用户名
private String ip; //ip地址
private String createDate; //操作时间
private String browser;//浏览器类型
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getMethodDescribe() {
return MethodDescribe;
}
public void setMethodDescribe(String methodDescribe) {
MethodDescribe = methodDescribe;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getCreateDate() {
return createDate;
}
public void setCreateDate(String createDate) {
this.createDate = createDate;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
}
SysLogService接口
package com.hjl.springsecurity.service;
import com.hjl.springsecurity.entity.SysLog;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 15:33
*/
public interface SysLogService {
//添加用户操作日志
void InsertSysLog(SysLog sysLog);
}
SysLogServiceImpl.java
package com.hjl.springsecurity.service.impl;
import com.hjl.springsecurity.dao.SysLogMapper;
import com.hjl.springsecurity.entity.SysLog;
import com.hjl.springsecurity.service.SysLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 15:34
*/
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogMapper sysLogMapper;
@Override
public void InsertSysLog(SysLog sysLog) {
sysLogMapper.InsertSysLog(sysLog);
}
}
SysLogMapper接口
package com.hjl.springsecurity.dao;
import com.hjl.springsecurity.entity.SysLog;
import org.apache.ibatis.annotations.Mapper;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 15:44
*/
@Mapper
public interface SysLogMapper {
void InsertSysLog(SysLog sysLog);
}
SysLogMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hjl.springsecurity.dao.SysLogMapper">
<insert id="InsertSysLog" parameterType="com.hjl.springsecurity.entity.SysLog">
INSERT INTO sys_log(uri,method,MethodDescribe,params,username,ip,create_date,browser) VALUES(#{uri},#{method},#{MethodDescribe},#{params},#{username},#{ip},#{createDate},#{browser});
</insert>
</mapper>
application.yml配置
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Shanghai&allowMultiQueries=true&characterEncoding=utf-8
password: root
username: root
devtools:
restart:
additional-paths: src/main/java
enabled: true
thymeleaf:
cache: true
#切面启用
aop:
proxy-target-class: true
auto: true
logging:
level:
com:
hjl:
springsecurity:
dao: debug
mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:/mapper/*.xml
4.自定义操作日志的注解类SystemLogService和SystemLogController
aop主要是以下代码
package com.hjl.springsecurity.config;
import java.lang.annotation.*;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 10:22
*/
@Target({
ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLogService {
String description() default "";
}
package com.hjl.springsecurity.controller;
import java.lang.annotation.*;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 10:20
*/
@Target({
ElementType.PARAMETER, ElementType.METHOD})//作用在参数和方法上
@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Documented//表明这个注解应该被 javadoc工具记录
public @interface SystemLogController {
String description() default "";
}
5.切面类SystemLogAspect.java
package com.hjl.springsecurity.config;
import com.hjl.springsecurity.controller.SystemLogController;
import com.hjl.springsecurity.entity.SysLog;
import com.hjl.springsecurity.entity.SysUser;
import com.hjl.springsecurity.service.SysLogService;
import com.hjl.springsecurity.util.IpUtiles;
import com.hjl.springsecurity.util.JsonUtiles;
import nl.bitwalker.useragentutils.Browser;
import nl.bitwalker.useragentutils.UserAgent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
/**
* @Author: hjl
* @Date: 2020/11/13 0013 10:22
*/
@Aspect
@Component
@SuppressWarnings("all")
public class SystemLogAspect {
@Autowired
private SysLogService sysLogService;
//本地异常日志记录对象
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
//Service层切点
@Pointcut("@annotation(com.hjl.springsecurity.config.SystemLogService)")
public void serviceAspect(){
}
//Controller层切点
@Pointcut("@annotation(com.hjl.springsecurity.controller.SystemLogController)")
public void controllerAspect(){
}
/**
* @Description 前置通知 用于拦截Controller层记录用户的操作
* @date 2018年9月3日 10:38
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//读取用户
String name = SecurityContextHolder.getContext().getAuthentication().getName();
//获取ip
String ip = IpUtiles.getRealIp(request);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
try {
//*========控制台输出=========*//
System.out.println("==============前置通知开始==============");
System.out.println("请求接口 uri:"+request.getRequestURI().toString()); //接口
// logger.info("URL : " + request.getRequestURL().toString()); //完整地址
System.out.println("请求方式 method:"+request.getMethod());
// System.out.println("请求方法:" + joinPoint.getSignature().toString());
System.out.println("方法描述 method_describe:" + getControllerMethodDescription(joinPoint));
System.out.println("参数信息 params:" + Arrays.toString(joinPoint.getArgs()));
System.out.println("请求人 username:"+name);
System.out.println("请求 ip:"+ip);
System.out.println("请求时间 create_date:"+df.format(new Date()));
String ua = request.getHeader("User-Agent");
//转成UserAgent对象
UserAgent userAgent = UserAgent.parseUserAgentString(ua);
//获取浏览器信息
Browser browser = userAgent.getBrowser();
String browserName = browser.getName();
System.out.println("浏览器 browser:"+browserName);
//*========数据库日志=========*//
SysLog sysLog=new SysLog();
sysLog.setUri(request.getRequestURI().toString());//请求接口
sysLog.setMethod(request.getMethod());//请求方式
sysLog.setMethodDescribe(getControllerMethodDescription(joinPoint));//描述
sysLog.setParams(Arrays.toString(joinPoint.getArgs()));//参数信息
sysLog.setUsername(name);//请求人
sysLog.setIp(ip);//ip
sysLog.setCreateDate(df.format(new Date()));//请求时间
sysLog.setBrowser(browserName);//浏览器类型
sysLogService.InsertSysLog(sysLog);
}catch (Exception e){
//记录本地异常日志
logger.error("==前置通知异常==");
logger.error("异常信息:{}",e.getMessage());
}
}
@AfterReturning(returning = "ret", pointcut = "controllerAspect()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
logger.info("RESPONSE : " + ret);
}
/**
* @Description 异常通知 用于拦截service层记录异常日志
* @date 2018年9月3日 下午5:43
*/
@AfterThrowing(pointcut = "serviceAspect()",throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint,Throwable e){
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();
//读取session中的用户
SysUser user = (SysUser) session.getAttribute("user");
//获取请求ip
String ip = IpUtiles.getRealIp(request);
//获取用户请求方法的参数并序列化为JSON格式字符串
String params = "";
if (joinPoint.getArgs()!=null&&joinPoint.getArgs().length>0){
for (int i = 0; i < joinPoint.getArgs().length; i++) {
params+= JsonUtiles.objectToJson(joinPoint.getArgs()[i])+";";
}
}
try{
/*========控制台输出=========*/
System.out.println("=====异常通知开始=====");
System.out.println("异常代码:" + e.getClass().getName());
System.out.println("异常信息:" + e.getMessage());
System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
System.out.println("方法描述:" + getServiceMethodDescription(joinPoint));
System.out.println("请求人:" + user.getName());
System.out.println("请求IP:" + ip);
System.out.println("请求参数:" + params);
/*==========数据库日志=========*/
}catch (Exception ex){
//记录本地异常日志
logger.error("==异常通知异常==");
logger.error("异常信息:{}", ex.getMessage());
}
}
/**
* @Description 获取注解中对方法的描述信息 用于service层注解
* @date 2018年9月3日 下午5:05
*/
public static String getServiceMethodDescription(JoinPoint joinPoint)throws Exception{
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 description = "";
for (Method method:methods) {
if (method.getName().equals(methodName)){
Class[] clazzs = method.getParameterTypes();
if (clazzs.length==arguments.length){
description = method.getAnnotation(SystemLogService.class).description();
break;
}
}
}
return description;
}
/**
* @Description 获取注解中对方法的描述信息 用于Controller层注解
* @date 2018年9月3日 上午12:01
*/
public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
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 description = "";
for (Method method:methods) {
if (method.getName().equals(methodName)){
Class[] clazzs = method.getParameterTypes();
if (clazzs.length==arguments.length){
description = method.getAnnotation(SystemLogController.class).description();
break;
}
}
}
return description;
}
}
使用注解记录日志
@RequestMapping("/")
@SystemLogController(description = "主页") //添加
public String showHome(){
String name= SecurityContextHolder.getContext().getAuthentication().getName();
logger.info("当前登录用户:{}",name);
return "home.html";
}
效果:
—End
master2分支
github:SpringSecurity+Aop记录日志