Spring切面编程(AOP)-记录管理系统操作日志
2022-03-16 本文已影响0人
AC编程
一、注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OpLog {
/** 参数标签 */
String tag() default "" ;
/** 参数名 */
String name() default "";
}
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OpLogs {
/** 子注解 */
OpLog[] params() ;
/** 链接因子 */
String factor() default "";
/** 操作模块 */
String module() default "";
/** 动作 */
String operation() default "";
}
二、注解使用类
@PostMapping("add")
@ApiOperation("新增")
@OpLogs(module = "广告位", operation = "新增", params = {
@OpLog(name = "name", tag = "广告位名称"),
@OpLog(name = "code", tag = "广告位编码")
})
public Result<Boolean> add(@Valid @RequestBody AdvertSiteAddVO vo) {
return Result.success(service.save(vo));
}
三、AOP拦截类
@Aspect
@Component
@Slf4j
public class LogOperation {
private static final String PN = "平台操作日志, ";
@Resource
private LogSender sender;
/**
* 日志切入点
*/
@Pointcut("@annotation(com.alanchen.annon.log.OpLogs)")
public void cut() {
}
/**
* 正常执行
* @param point 切入点
* @throws Throwable 异常信息
*/
@AfterReturning(value = "cut()", returning = "result")
public void afterReturning(JoinPoint point, Result result) {
try {
if(ResultCode.SUCCESS.getCode() != result.getCode()) {
return;
}
String user = null;
//从Header中获取用户信息
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
String userHeader = request.getHeader("user");
String userAttribute = request.getAttribute("user") != null ? (String) request.getAttribute("user") : null;
log.info(PN + "userHeader: " + userHeader + ", userAttribute: " + userAttribute);
if (StrUtil.isNotEmpty(userAttribute)) {
user = userAttribute;
}else if(StrUtil.isNotEmpty(userHeader)){
user = userHeader;
}
if (user == null) {
log.error(PN + "不生成操作记录, 原因: header中的用户信息不存在");
return;
}
try {
user = URLDecoder.decode(user, "UTF-8");
} catch (UnsupportedEncodingException ex) {
log.error(PN + "不生成操作记录, 原因: 针对token信息进行URLDecode失败");
}
SecurityUserDTO suser = JSON.parseObject(user, SecurityUserDTO.class);
if (SecurityUserTypeEnum.APP.equals(suser.getUserType())) {
return;
}
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
OpLogs opLogs = method.getAnnotation(OpLogs.class);
if (opLogs == null) {
return;
}
//模块
String module = opLogs.module();
//操作
String operation = opLogs.operation();
//自定义参数
OpLog[] params = opLogs.params();
String op = operation + this.parseBehavior(request, params);
String ip = ip(request);
String uri = request.getRequestURI();
LogAddVO vo = new LogAddVO(suser.getId(), module, op, ip, uri, LocalDateTime.now());
sender.asyncSend(vo);
} catch (Exception ex) {
//捕获到异常时, 直接输出异常信息, 不在往外抛出, 防止被全局异常处理捕获
log.error(PN + "发生异常: " + ex.getMessage() + ", 异常信息: " + ex.getStackTrace()[0].toString());
}
}
/**
* 解析行为
* @param request HttpServletRequest
* @param params 子注解列表
*/
private String parseBehavior(HttpServletRequest request, OpLog[] params) {
StringBuffer sb = new StringBuffer();
sb.append("[");
String method = request.getMethod();
//处理POST请求
if (HttpMethod.POST.name().equals(method)) {
sb.append(parseBehaviorPost(new CustomHttpServletRequestWrapper(request), params));
}
//处理PUT请求
else if (HttpMethod.PUT.name().equals(method)) {
}
//处理DELETE请求
else if (HttpMethod.DELETE.name().equals(method)) {
sb.append(parseBehaviorDelete(new CustomHttpServletRequestWrapper(request), params));
}
sb.append("]");
String buhavior = sb.toString();
return buhavior.endsWith("[]") ? buhavior.replace("[]", "") : buhavior;
}
private String parseBehaviorDelete(CustomHttpServletRequestWrapper requestWrapper, OpLog[] params) {
StringBuffer sb = new StringBuffer();
Map<String, String[]> parameterMap = requestWrapper.getParameterMap();
if (parameterMap != null || parameterMap.size() != 0) {
Iterator<Map.Entry<String, String[]>> iterator = parameterMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String[]> next = iterator.next();
for (int i=0; i<params.length; i++) {
OpLog param = params[i];
//参数标签
String tag = param.tag();
//参数值
String name = param.name();
if (name.equalsIgnoreCase(next.getKey())) {
String[] value = next.getValue();
sb.append(tag).append(": ").append(value[0]);
if (i < (params.length - 1)) {
sb.append(", ");
}
}
}
}
}
return sb.toString();
}
private String parseBehaviorPost(CustomHttpServletRequestWrapper requestWrapper, OpLog[] params) {
StringBuffer sb = new StringBuffer();
String body = requestWrapper.getBody();
if (StrUtil.isNotBlank(body)) {
JSONObject jsonObject = JSONUtil.parseObj(body);
Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> next = iterator.next();
for (int i=0; i<params.length; i++) {
OpLog param = params[i];
//参数标签
String tag = param.tag();
//参数值
String name = param.name();
if (name.equalsIgnoreCase(next.getKey())) {
sb.append(tag).append(": ").append(next.getValue());
if (i < (params.length - 1)) {
sb.append(", ");
}
}
}
}
}
return sb.toString();
}
/**
* 获取ip
* @param request
* @return
*/
private String ip(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String comma = ",";
String localhost = "127.0.0.1";
if (ip.contains(comma)) {
ip = ip.split(",")[0];
}
if (localhost.equals(ip)) {
// 获取本机真正的ip地址
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error(e.getMessage(), e);
}
}
return ip;
}
}
四、MQ记录操作日志
@Slf4j
@Component
public class LogSender {
private static final String PN = "运营平台操作记录发送, ";
@Resource
private RocketMQTemplate rocketMQTemplate;
@Value("${rocketmq.topic.sysLog}")
private String topic;
/**
* 发送消息(异步)
*/
public void asyncSend(LogAddVO vo) {
String msg = JSONObject.toJSONString(vo);
rocketMQTemplate.asyncSend(topic, msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info(PN + "消息发送成功, result: {}", sendResult);
}
@Override
public void onException(Throwable e) {
log.error(PN + "消息发送失败");
e.printStackTrace();
}
});
}
}
五、操作日志记录表
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuppressWarnings("serial")
@TableName("t_sys_log")
public class Log{
/** 操作用户ID */
private Long userId;
/** 操作用户名 */
private String userName;
/** 操作用户手机号 */
private String userMobile;
/** 角色ID */
private String roleId;
/** 角色名 */
private String roleName;
/** 一级菜单ID */
private Long menu1Id;
/** 一级菜单 */
private String menu1;
/** 二级菜单ID */
private Long menu2Id;
/** 二级菜单 */
private String menu2;
/** 操作 */
private String op;
/** 操作模块 */
private String opMod;
/** 操作IP地址 */
private String ip;
}
六、HttpServletRequestWrapper
@Slf4j
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final String body;
private final Map<String, String> headers;
public CustomHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
body = buildBody(request);
headers = buildHeaders(request);
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
/**
* 重写 setHeader
* @param name header name
* @param value header value
*/
public void setHeader(String name, String value){
headers.put(name, value);
}
public String getHeader(String name) {
String headerValue = headers.get(name);
if (headerValue != null){
return headerValue;
}
return ((HttpServletRequest) getRequest()).getHeader(name);
}
public Enumeration<String> getHeaderNames() {
Set<String> set = new HashSet<>(headers.keySet());
Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
while (e.hasMoreElements()) {
String n = e.nextElement();
set.add(n);
}
return Collections.enumeration(set);
}
public String getBody() {
return this.body;
}
private String buildBody(HttpServletRequest request) {
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[1024];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
return stringBuilder.toString();
}
private Map<String, String> buildHeaders(HttpServletRequest request) {
Map<String, String> headers = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
headers.put(headerName, headerValue);
}
return headers;
}
}