#
vincentlu
2 天以前 313f81780f554a4962f131bbd6ab795c24d1edfe
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
143
144
145
146
147
148
149
150
151
152
153
154
155
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.Value;
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.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
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,
                namespaceType.name,
                handlerMethod.getBeanType().getSimpleName() + "#" + handlerMethod.getMethod().getName(),
                request.getMethod(),
                request.getRequestURI(),
                request.getQueryString(),
                System.currentTimeMillis(),
                buildRequestCache(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 Map<String, Object> buildRequestCache(HttpServletRequest request) {
        Map<String, Object> cache = new LinkedHashMap<>();
        cache.put("method", request.getMethod());
        cache.put("uri", request.getRequestURI());
        cache.put("query", request.getQueryString());
        cache.put("contentType", request.getContentType());
        cache.put("parameters", flattenParameters(request.getParameterMap()));
        cache.put("headers", extractHeaders(request));
        String body = readBody(request);
        if (!Cools.isEmpty(body)) {
            cache.put("body", body);
        }
        return Collections.unmodifiableMap(cache);
    }
 
    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 Map<String, Object> extractHeaders(HttpServletRequest request) {
        Map<String, Object> headers = new LinkedHashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames == null) {
            return headers;
        }
        List<String> names = Collections.list(headerNames);
        for (String name : names) {
            headers.put(name, request.getHeader(name));
        }
        return headers;
    }
 
    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;
        }
    }
 
    @Value
    public static class IntegrationRequestContext {
        NamespaceType namespaceType;
        String namespace;
        String handler;
        String method;
        String uri;
        String query;
        long startAt;
        Map<String, Object> requestSnapshot;
    }
}