http://jira.mfd.msk:8088/browse/BKI-3215 Added CachedHttpRequestWrapper to handle request body before doFilter invocation; Added memberCode ro logs; Updated version to 1.17

This commit is contained in:
Mikhail Trofimov 2025-10-06 15:37:27 +03:00
parent b61970a71e
commit 3b9c2eaa15
5 changed files with 125 additions and 14 deletions

View file

@ -1,5 +1,5 @@
#commons
APP_VERSION=1.16
APP_VERSION=1.17
REGISTRY_URL=rh8-vm3.nbki.msk:5000
JMX_PORT=
JMX_HOSTNAME=

View file

@ -10,7 +10,7 @@
</parent>
<groupId>ru.nbch</groupId>
<artifactId>credit-tracker</artifactId>
<version>1.16</version>
<version>1.17</version>
<name>credit_tracker</name>
<description>Credit Tracker Application</description>

View file

@ -3,6 +3,7 @@ package ru.nbch.credit_tracker.config.rest.filters;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import lombok.extern.slf4j.Slf4j;
@ -12,11 +13,18 @@ import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import ru.nbch.credit_tracker.config.rest.wrappers.CachedHttpRequestWrapper;
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Component
@Slf4j
@Order(0)
@ -25,6 +33,12 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
private static final Set<String> SENSITIVE_HEADERS = Set.of("authorization", "cookie", "set-cookie");
private static final int MAX_BODY = 10 * 1024;
private final IXmlProcessor xmlProcessor;
public HttpLoggingFilter(IXmlProcessor xmlProcessor) {
this.xmlProcessor = xmlProcessor;
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
@ -38,7 +52,7 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
long startNs = System.nanoTime();
ContentCachingRequestWrapper req = wrapRequest(request);
HttpServletRequestWrapper req = wrapRequest(request);
ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);
String correlationId = Optional.ofNullable(request.getHeader("X-Correlation-Id"))
@ -46,14 +60,15 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
.orElse(UUID.randomUUID().toString());
resp.setHeader("X-Correlation-Id", correlationId);
logIncomingRequest(req, correlationId);
String memberCode = parseMemberCode(req);
logIncomingRequest(req, correlationId, memberCode);
try {
chain.doFilter(req, resp);
} finally {
long tookMs = (System.nanoTime() - startNs) / 1_000_000;
Map<String, String> reqParams = paramsMap(req);
Map<String, String> reqParams = paramsMap(req, memberCode);
Map<String, String> reqHeaders = headersMap(req);
Map<String, String> respHeaders = headersMap(resp);
@ -62,10 +77,9 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
mask(respHeaders);
log.info(
"HTTP (RESPONSE) {} {}{} | status={} | took={}ms | cid={} | reqHeaders={} | reqParams={} | respHeaders={}",
"HTTP (RESPONSE) {} {} | status={} | took={}ms | cid={} | reqHeaders={} | reqParams={} | respHeaders={}",
req.getMethod(),
req.getRequestURI(),
req.getQueryString() == null ? "" : "?" + req.getQueryString(),
resp.getStatus(),
tookMs,
correlationId,
@ -78,34 +92,75 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
}
}
private String parseMemberCode(HttpServletRequestWrapper req) {
String memberCode = null;
if (!(req instanceof CachedHttpRequestWrapper)) {
return null;
}
private void logIncomingRequest(ContentCachingRequestWrapper req, String correlationId) {
Map<String, String> reqParams = paramsMap(req);
String body = ((CachedHttpRequestWrapper) req).getBody();
if (isBlank(body)) {
return null;
}
switch (req.getRequestURI()) {
case "/api/v1/signal/active_packages":
try {
MonitoringActivePackagesRequest activePackagesRequest = xmlProcessor.read(body, MonitoringActivePackagesRequest.class);
if (activePackagesRequest != null && isNotBlank(activePackagesRequest.getUserId())) {
memberCode = activePackagesRequest.getUserId().substring(0, 6);
}
} catch (Exception e) {
memberCode = null;
}
break;
case "/api/v1/signal/all_packages":
try {
MonitoringAllPackagesRequest allPackagesRequest = xmlProcessor.read(body, MonitoringAllPackagesRequest.class);
if (allPackagesRequest != null && isNotBlank(allPackagesRequest.getUserId())) {
memberCode = allPackagesRequest.getUserId().substring(0, 6);
}
} catch (Exception e) {
memberCode = null;
}
break;
default:
break;
}
return memberCode;
}
private void logIncomingRequest(HttpServletRequestWrapper req, String correlationId, String memberCode) {
Map<String, String> reqParams = paramsMap(req, memberCode);
Map<String, String> reqHeaders = headersMap(req);
mask(reqHeaders);
log.info(
"HTTP (REQUEST) {} {}{} | cid={} | reqHeaders={} | reqParams={}",
"HTTP (REQUEST) {} {} | cid={} | reqHeaders={} | reqParams={}",
req.getMethod(),
req.getRequestURI(),
req.getQueryString() == null ? "" : "?" + req.getQueryString(),
correlationId,
reqHeaders,
reqParams
);
}
private static ContentCachingRequestWrapper wrapRequest(HttpServletRequest request) {
return (request instanceof ContentCachingRequestWrapper c) ? c : new ContentCachingRequestWrapper(request, MAX_BODY);
private static HttpServletRequestWrapper wrapRequest(HttpServletRequest request) throws IOException {
return switch (request.getRequestURI()) {
case "/api/v1/signal/active_packages", "/api/v1/signal/all_packages" ->
new CachedHttpRequestWrapper(request);
default ->
(request instanceof ContentCachingRequestWrapper c) ? c : new ContentCachingRequestWrapper(request, MAX_BODY);
};
}
private static Map<String, String> headersMap(HttpServletRequest req) {
return Collections.list(req.getHeaderNames()).stream()
.collect(Collectors.toMap(h -> h, req::getHeader, (a, b) -> a, LinkedHashMap::new));
}
private Map<String, String> paramsMap(ContentCachingRequestWrapper req) {
private static Map<String, String> paramsMap(HttpServletRequestWrapper req, String memberCode) {
Map<String, String> paramsMap = new LinkedHashMap<>();
req.getParameterMap().forEach((k, v) -> {
String params = String.join(", ", v);
@ -120,6 +175,11 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
} catch (Exception e) {
// ignore
}
if (memberCode != null) {
paramsMap.put("memberCode", memberCode);
}
return paramsMap;
}

View file

@ -0,0 +1,47 @@
package ru.nbch.credit_tracker.config.rest.wrappers;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.Getter;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@Getter
public class CachedHttpRequestWrapper extends HttpServletRequestWrapper {
private final String body;
public CachedHttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = IOUtils.toString(request.getReader());
}
@Override
public ServletInputStream getInputStream() {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(getBody().getBytes());
return new ServletInputStream() {
public int read() {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
}
}

View file

@ -6,6 +6,8 @@ import org.springframework.context.annotation.Configuration;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_file.SgnlReportMonitorigSetupError;
import ru.nbch.credit_tracker.entities.external_response.online_report.SgnlOnlineReport;
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringError;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
@ -26,6 +28,8 @@ public class XmlConfig {
// unmarshalling classes
SgnlOnlineReport.class,
SgnlReportMonitorigSetupError.class,
MonitoringActivePackagesRequest.class,
MonitoringAllPackagesRequest.class,
// marshalling classes
MonitoringError.class,
MonitoringSetupResponse.class);