接口请求日志记录案例

前言

记录一下 siem_log 接口请求日志记录设计案例

一、 siem_log DDL,Java Entity

CREATE TABLE `dem_siem_log` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '日志ID',
  `username` varchar(50) DEFAULT NULL COMMENT '操作用户',
  `param` mediumtext COMMENT '方法参数',
  `module` varchar(64) NOT NULL,
  `url` varchar(256) NOT NULL,
  `event_type` varchar(32) NOT NULL,
  `event_desc` varchar(256) NOT NULL,
  `device_type` varchar(64) NOT NULL,
  `ip` varchar(64) DEFAULT NULL COMMENT '操作者IP',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `result` varchar(128) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=238225 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("dem_siem_log")
public class SIEMLogDO {

    //SIEM: 主体
    private String username;
    //SIEM: 主体IP
    private String ip;
    //SIEM: 时间
    private Date createTime;
    //SIEM: 模块
    private String module;
    //SIEM: 事件来源
    @Builder.Default
    private String deviceType = "web";
    //SIEM: 接口路径
    private String url;
    //SIEM: 事件类型
    private String eventType;
    //SIEM: 事件描述
    private String eventDesc;
    //SIEM: 执行结果
    private String result;
    //SIEM: 原始请求参数
    @Builder.Default
    private String param = "";

}

二、 注解和切面

(一)注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SIEMLog {

    String desc();

    EventType eventType();

    String[] requestMaskFields() default {};

    public enum EventType {

        ADD("add"), DELETE("delete"), EDIT("edit"), SEARCH("search"), VIEW("view"), LOGIN("login"), LOGOUT("logout");

        @Getter
        private String name;

        EventType (String name) {
            this.name = name;
        }
    }
}

(二)切面

@Slf4j
@Aspect
@Component
public class SIEMLogAspect {

    private static final String MODULE = "dem-backend";

    @Autowired
    private SIEMLogManagerImpl siemLogManager;
    @Autowired
    private UserSessionHolder userSessionHolder;

    @Pointcut("@annotation(com.aexpec.uds.common.annotation.SIEMLog)")
    public void pointcut() {
        // do nothing
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        String username = (String) userSessionHolder.getAttribute(UserSessionHolder.SESSION_USERNAME);
        Object returnValue = point.proceed();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip = IPUtil.getIpAddress(request);
        if (username == null) {
            username = (String) userSessionHolder.getAttribute(UserSessionHolder.SESSION_USERNAME);
        }
        if (StringUtils.isBlank(username)) {
            log.warn("cannot get username from session");
            return returnValue;
        }
        MethodSignature methodSign = (MethodSignature) point.getSignature();
        SIEMLog logAnnotation = methodSign.getMethod().getAnnotation(SIEMLog.class);

        EventDetail.EventDetailBuilder detailBuilder = EventDetail.builder();
        if (point.getArgs().length > 0) {
            try {
                //敏感字段掩码记录到日志中
                String[] maskFields = logAnnotation.requestMaskFields();
                Object arg = point.getArgs()[0];
                if (maskFields.length > 0) {
                    Field[] declaredFields = arg.getClass().getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        for (String maskField : maskFields) {
                            if (declaredField.getName().equals(maskField)) {
                                declaredField.setAccessible(true);
                                declaredField.set(arg, "******");
                            }
                        }
                    }
                }
                detailBuilder.input(new ObjectMapper().writeValueAsString(arg));
            } catch (Exception e) {
                log.warn("unable to serialize request params");
            }
        }


        String result = "success";
        if (returnValue instanceof CommonResponse) {
            CommonResponse response = ((CommonResponse) returnValue);
            int code = response.getCode();
            if (code != ErrorCode.SUCCESS.getCode()) {
                result = "failure";
            }
            detailBuilder.output(new ObjectMapper().writeValueAsString(response.getData()));
        }
        String logMsg = new ObjectMapper().writeValueAsString(detailBuilder.build());
        //记录siem日志
        siemLogManager.save(SIEMLogDO.builder()
                .createTime(new Date())
                .eventDesc(logAnnotation.desc())
                .eventType(logAnnotation.eventType().getName())
                .ip(ip)
                .module(MODULE)
                .username(username)
                .url(request.getRequestURI())
                .result(result)
                .param(logMsg.substring(0, Math.min(logMsg.length(), 4096)))
                .build());

        return returnValue;
    }

    @Data
    @Builder
    public static class EventDetail {
        private String input;
        private String output;
    }
}

(三)IPUtils

public class IPUtil {
    private static final String UNKNOWN = "unknown";

    protected IPUtil() {
    }

    public static String getIpAddress(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.getHeader("HTTP_CLIENT_IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return ip.split(",")[0];
    }
}

三、controller层使用

    @PostMapping("/getFlow")
    @SIEMLog(desc = "获取流程主信息", eventType = SIEMLog.EventType.SEARCH)
    public CommonResponse<FlowMain> getFlow(@Valid @RequestBody GetTableFlowRequest request) {
        FlowMain flowMain = approveService.getFlowMain(request.getFlowNo());
        if (0 == request.getType()) {
            String newTableInfo = getNewTableInfo(flowMain);
            flowMain.setSubmitInfo(newTableInfo);
        }
        return CommonResponse.success(flowMain);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值