♻️ Refactoring code. 重构 sys-log 支持 body 入库

This commit is contained in:
lbw 2023-08-11 16:06:43 +08:00
parent b1c53f18e7
commit f111e7f454
14 changed files with 843 additions and 594 deletions

View File

@ -246,8 +246,8 @@ CREATE TABLE `sys_log` (
`log_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '日志类型',
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日志标题',
`service_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '服务ID',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '远程地址',
@ -264,15 +264,6 @@ CREATE TABLE `sys_log` (
KEY `sys_log_create_date` (`create_time`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='日志表';
-- ----------------------------
-- Records of sys_log
-- ----------------------------
BEGIN;
INSERT INTO `sys_log` VALUES (1677218733317345282, '0', '更新角色菜单', NULL, 'anonymousUser', ' ', '2023-07-07 15:31:22', NULL, NULL, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', '/role/menu', 'PUT', '', 46, '0', NULL);
INSERT INTO `sys_log` VALUES (1677218768511750146, '0', '更新角色菜单', NULL, 'anonymousUser', ' ', '2023-07-07 15:31:30', NULL, NULL, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', '/role/menu', 'PUT', '', 59, '0', NULL);
INSERT INTO `sys_log` VALUES (1677218849554092033, '0', '更新角色菜单', NULL, 'anonymousUser', ' ', '2023-07-07 15:31:50', NULL, NULL, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', '/role/menu', 'PUT', '', 45, '0', NULL);
INSERT INTO `sys_log` VALUES (1677218871825846274, '0', '更新角色菜单', NULL, 'anonymousUser', ' ', '2023-07-07 15:31:55', NULL, NULL, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', '/role/menu', 'PUT', '', 9, '0', NULL);
COMMIT;
-- ----------------------------
-- Table structure for sys_menu

View File

@ -54,5 +54,9 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -18,9 +18,10 @@ package com.pig4cloud.pig.common.log;
import com.pig4cloud.pig.admin.api.feign.RemoteLogService;
import com.pig4cloud.pig.common.log.aspect.SysLogAspect;
import com.pig4cloud.pig.common.log.config.PigLogProperties;
import com.pig4cloud.pig.common.log.event.SysLogListener;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@ -30,14 +31,14 @@ import org.springframework.scheduling.annotation.EnableAsync;
* @date 2019/2/1 日志自动配置
*/
@EnableAsync
@RequiredArgsConstructor
@ConditionalOnWebApplication
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PigLogProperties.class)
@ConditionalOnProperty(value = "security.log.enabled", matchIfMissing = true)
public class LogAutoConfiguration {
@Bean
public SysLogListener sysLogListener(RemoteLogService remoteLogService) {
return new SysLogListener(remoteLogService);
public SysLogListener sysLogListener(PigLogProperties logProperties, RemoteLogService remoteLogService) {
return new SysLogListener(remoteLogService, logProperties);
}
@Bean

View File

@ -17,11 +17,12 @@
package com.pig4cloud.pig.common.log.aspect;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.log.event.SysLogEvent;
import com.pig4cloud.pig.common.log.event.SysLogEventSource;
import com.pig4cloud.pig.common.log.util.LogTypeEnum;
import com.pig4cloud.pig.common.log.util.SysLogUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
@ -37,6 +38,7 @@ import org.springframework.expression.EvaluationContext;
*/
@Aspect
@Slf4j
@RequiredArgsConstructor
public class SysLogAspect {
@Around("@annotation(sysLog)")
@ -62,9 +64,12 @@ public class SysLogAspect {
}
}
SysLog logVo = SysLogUtils.getSysLog();
SysLogEventSource logVo = SysLogUtils.getSysLog();
logVo.setTitle(value);
// 获取请求body参数
if (StrUtil.isBlank(logVo.getParams())) {
logVo.setBody(point.getArgs());
}
// 发送异步日志事件
Long startTime = System.currentTimeMillis();
Object obj;

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2019-2029, Dreamlu 卢春梦 (596392912@qq.com & www.dreamlu.net).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.common.log.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* 日志配置类
*
* @author L.cm
*/
@Getter
@Setter
@ConfigurationProperties(PigLogProperties.PREFIX)
public class PigLogProperties {
public static final String PREFIX = "security.log";
/**
* 开启日志记录
*/
private boolean enabled = true;
/**
* 放行字段password,mobile,idcard,phone
*/
@Value("${security.log.exclude-fields:password,mobile,idcard,phone}")
private List<String> excludeFields;
/**
* 请求报文最大存储长度
*/
private Integer maxLength = 2000;
}

View File

@ -0,0 +1,20 @@
package com.pig4cloud.pig.common.log.event;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import lombok.Data;
/**
* spring event log
*
* @author lengleng
* @date 2023/8/11
*/
@Data
public class SysLogEventSource extends SysLog {
/**
* 参数重写成object
*/
private Object body;
}

View File

@ -16,30 +16,74 @@
package com.pig4cloud.pig.common.log.event;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import com.pig4cloud.pig.admin.api.feign.RemoteLogService;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.jackson.PigJavaTimeModule;
import com.pig4cloud.pig.common.log.config.PigLogProperties;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import java.util.Objects;
/**
* @author lengleng 异步监听日志事件
*/
@Slf4j
@RequiredArgsConstructor
public class SysLogListener {
public class SysLogListener implements InitializingBean {
// new 一个 避免日志脱敏策略影响全局ObjectMapper
private final static ObjectMapper objectMapper = new ObjectMapper();
private final RemoteLogService remoteLogService;
private final PigLogProperties logProperties;
@SneakyThrows
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event) {
SysLog sysLog = (SysLog) event.getSource();
SysLogEventSource source = (SysLogEventSource) event.getSource();
SysLog sysLog = new SysLog();
BeanUtils.copyProperties(source, sysLog);
// json 格式刷参数放在异步中处理提升性能
if (Objects.nonNull(source.getBody())) {
String params = objectMapper.writeValueAsString(source.getBody());
sysLog.setParams(StrUtil.subPre(params, logProperties.getMaxLength()));
}
remoteLogService.saveLog(sysLog, SecurityConstants.FROM_IN);
}
@Override
public void afterPropertiesSet() {
objectMapper.addMixIn(Object.class, PropertyFilterMixIn.class);
String[] ignorableFieldNames = logProperties.getExcludeFields().toArray(new String[0]);
FilterProvider filters = new SimpleFilterProvider().addFilter("filter properties by name",
SimpleBeanPropertyFilter.serializeAllExcept(ignorableFieldNames));
objectMapper.setFilterProvider(filters);
objectMapper.registerModule(new PigJavaTimeModule());
}
@JsonFilter("filter properties by name")
class PropertyFilterMixIn {
}
}

View File

@ -16,9 +16,15 @@
package com.pig4cloud.pig.common.log.util;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.HttpUtil;
import com.pig4cloud.pig.admin.api.entity.SysLog;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import com.pig4cloud.pig.common.log.config.PigLogProperties;
import com.pig4cloud.pig.common.log.event.SysLogEventSource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.experimental.UtilityClass;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
@ -29,10 +35,12 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
/**
@ -43,16 +51,23 @@ import java.util.Objects;
@UtilityClass
public class SysLogUtils {
public SysLog getSysLog() {
public SysLogEventSource getSysLog() {
HttpServletRequest request = ((ServletRequestAttributes) Objects
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
SysLog sysLog = new SysLog();
SysLogEventSource sysLog = new SysLogEventSource();
sysLog.setLogType(LogTypeEnum.NORMAL.getType());
sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
sysLog.setMethod(request.getMethod());
sysLog.setRemoteAddr(JakartaServletUtil.getClientIP(request));
sysLog.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
sysLog.setCreateBy(getUsername());
sysLog.setServiceId(getClientId());
// get 参数脱敏
PigLogProperties logProperties = SpringContextHolder.getBean(PigLogProperties.class);
Map<String, String[]> paramsMap = MapUtil.removeAny(request.getParameterMap(),
ArrayUtil.toArray(logProperties.getExcludeFields(), String.class));
sysLog.setParams(HttpUtil.toParams(paramsMap));
return sysLog;
}
@ -100,4 +115,22 @@ public class SysLogUtils {
return context;
}
/**
* 获取客户端
* @return clientId
*/
private String getClientId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
}
Object principal = authentication.getPrincipal();
if (principal instanceof OAuth2AuthenticatedPrincipal) {
OAuth2AuthenticatedPrincipal auth2Authentication = (OAuth2AuthenticatedPrincipal) principal;
return MapUtil.getStr(auth2Authentication.getAttributes(), SecurityConstants.CLIENT_ID);
}
return null;
}
}

View File

@ -0,0 +1,30 @@
{
"groups": [
{
"name": "security.log",
"type": "com.pig4cloud.pig.common.log.config.PigLogProperties",
"sourceType": "com.pig4cloud.pig.common.log.config.PigLogProperties"
}
],
"properties": [
{
"name": "security.log.enabled",
"type": "java.lang.Boolean",
"description": "开启日志记录",
"sourceType": "com.pig4cloud.pig.common.log.config.PigLogProperties"
},
{
"name": "security.log.exclude-fields",
"type": "java.util.List<java.lang.String>",
"description": "放行字段password,mobile,idcard,phone",
"sourceType": "com.pig4cloud.pig.common.log.config.PigLogProperties"
},
{
"name": "security.log.max-length",
"type": "java.lang.Integer",
"description": "请求报文最大存储长度",
"sourceType": "com.pig4cloud.pig.common.log.config.PigLogProperties"
}
],
"hints": []
}

View File

@ -1,3 +0,0 @@
# https://github.com/spring-projects/spring-boot/issues/31252
org.springframework.boot.env.EnvironmentPostProcessor=\
com.pig4cloud.pig.common.log.init.ApplicationLoggerInitializer

View File

@ -1,6 +1,7 @@
package com.pig4cloud.pig.common.security.component;
import cn.hutool.extra.spring.SpringUtil;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.service.PigUser;
import com.pig4cloud.pig.common.security.service.PigUserDetailsService;
import lombok.RequiredArgsConstructor;
@ -70,7 +71,13 @@ public class PigCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector
catch (Exception ex) {
log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage());
}
return (PigUser) userDetails;
// 注入客户端信息方便上下文中获取
PigUser pigxUser = (PigUser) userDetails;
Objects.requireNonNull(pigxUser)
.getAttributes()
.put(SecurityConstants.CLIENT_ID, oldAuthorization.getRegisteredClientId());
return pigxUser;
}
}

View File

@ -36,6 +36,11 @@ public class PigUser extends User implements OAuth2AuthenticatedPrincipal {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
/**
* 扩展属性方便存放oauth 上下文相关信息
*/
private final Map<String, Object> attributes = new HashMap<>();
/**
* 用户ID
*/
@ -71,7 +76,7 @@ public class PigUser extends User implements OAuth2AuthenticatedPrincipal {
*/
@Override
public Map<String, Object> getAttributes() {
return new HashMap<>();
return this.attributes;
}
@Override

View File

@ -0,0 +1,58 @@
{
"groups": [
{
"name": "security.xss",
"type": "com.pig4cloud.pig.common.xss.config.PigXssProperties",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties"
}
],
"properties": [
{
"name": "security.xss.enable-escape",
"type": "java.lang.Boolean",
"description": "[clear 专用] 使用转义,默认关闭",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties",
"defaultValue": false
},
{
"name": "security.xss.enabled",
"type": "java.lang.Boolean",
"description": "开启xss",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties",
"defaultValue": true
},
{
"name": "security.xss.mode",
"type": "com.pig4cloud.pig.common.xss.config.PigXssProperties$Mode",
"description": "模式clear 清理默认escape 转义",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties"
},
{
"name": "security.xss.path-exclude-patterns",
"type": "java.util.List<java.lang.String>",
"description": "放行的路由,默认为空",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties"
},
{
"name": "security.xss.path-patterns",
"type": "java.util.List<java.lang.String>",
"description": "拦截的路由,默认为空",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties"
},
{
"name": "security.xss.pretty-print",
"type": "java.lang.Boolean",
"description": "[clear 专用] prettyPrint默认关闭 保留换行",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties",
"defaultValue": false
},
{
"name": "security.xss.trim-text",
"type": "java.lang.Boolean",
"description": "全局:对文件进行首尾 trim",
"sourceType": "com.pig4cloud.pig.common.xss.config.PigXssProperties",
"defaultValue": true
}
],
"hints": []
}