#
vincentlu
昨天 5b2e04269894ec4e6bcc1e3502bfa203e33cfe3a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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<String, Object> 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<String, Object> flattenParameters(Map<String, String[]> rawParams) {
        Map<String, Object> 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<String, Object> parameters;
        private final String body;
    }
}