package com.zy.acs.manager.common.interceptor; import com.zy.acs.framework.common.Cools; import com.zy.acs.manager.common.annotation.IntegrationAuth; import com.zy.acs.manager.core.domain.type.NamespaceType; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; /** * Intercepts open API calls so they can be audited via IntegrationRecordAdvice. */ @Slf4j @Component public class IntegrationOpenApiInterceptor implements HandlerInterceptor { private static final String ATTR_CONTEXT = IntegrationOpenApiInterceptor.class.getName() + ".CONTEXT"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (!(handler instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) handler; IntegrationAuth integrationAuth = findIntegrationAuth(handlerMethod); if (integrationAuth == null || integrationAuth.value() == IntegrationAuth.Enable.SKIP_LOG) { return true; } NamespaceType namespaceType = integrationAuth.name() == null ? NamespaceType.NONE : integrationAuth.name(); IntegrationRequestContext context = new IntegrationRequestContext( namespaceType, handlerMethod.getBeanType().getSimpleName() + "#" + handlerMethod.getMethod().getName(), System.currentTimeMillis(), buildPayload(request) ); request.setAttribute(ATTR_CONTEXT, context); return true; } public static IntegrationRequestContext getContext(HttpServletRequest request) { Object attribute = request.getAttribute(ATTR_CONTEXT); if (attribute instanceof IntegrationRequestContext) { return (IntegrationRequestContext) attribute; } return null; } private IntegrationAuth findIntegrationAuth(HandlerMethod handlerMethod) { IntegrationAuth annotation = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getMethod(), IntegrationAuth.class); if (annotation != null) { return annotation; } return AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), IntegrationAuth.class); } private RequestPayload buildPayload(HttpServletRequest request) { Map params = flattenParameters(request.getParameterMap()); String body = readBody(request); return new RequestPayload( request.getMethod(), request.getRequestURI(), request.getQueryString(), request.getContentType(), params.isEmpty() ? null : params, Cools.isEmpty(body) ? null : body ); } private Map flattenParameters(Map rawParams) { Map flattened = new LinkedHashMap<>(); if (rawParams == null) { return flattened; } rawParams.forEach((key, values) -> { if (values == null) { flattened.put(key, null); } else if (values.length == 1) { flattened.put(key, values[0]); } else { flattened.put(key, Arrays.asList(values)); } }); return flattened; } private String readBody(HttpServletRequest request) { if (request instanceof ContentCachingRequestWrapper) { ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request; byte[] buffer = wrapper.getContentAsByteArray(); if (buffer.length > 0) { Charset charset = charset(wrapper.getCharacterEncoding()); return new String(buffer, charset); } } return null; } private Charset charset(String encoding) { if (Cools.isEmpty(encoding)) { return StandardCharsets.UTF_8; } try { return Charset.forName(encoding); } catch (Exception e) { return StandardCharsets.UTF_8; } } @Getter @AllArgsConstructor public static class IntegrationRequestContext { private final NamespaceType namespaceType; private final String handler; private final long startAt; private final RequestPayload payload; } @Getter @AllArgsConstructor public static class RequestPayload { private final String method; private final String uri; private final String query; private final String contentType; private final Map parameters; private final String body; } }