From a258db21a45a5408218da533fae8bdf099a96dd7 Mon Sep 17 00:00:00 2001 From: lroyia Date: Wed, 25 Mar 2026 16:23:33 +0800 Subject: [PATCH] =?UTF-8?q?ims=E5=86=85=E5=AE=B9=E7=9A=84json=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E6=A0=BC=E5=BC=8F=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chinaweal/aiccs/config/FilterConfig.java | 13 ++ .../aiccs/config/ImsRequestFilter.java | 111 ++++++++++++++++++ src/main/resources/application.yml | 4 - 3 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/chinaweal/aiccs/config/ImsRequestFilter.java diff --git a/src/main/java/com/chinaweal/aiccs/config/FilterConfig.java b/src/main/java/com/chinaweal/aiccs/config/FilterConfig.java index 4877784..d40d094 100644 --- a/src/main/java/com/chinaweal/aiccs/config/FilterConfig.java +++ b/src/main/java/com/chinaweal/aiccs/config/FilterConfig.java @@ -36,4 +36,17 @@ public class FilterConfig { registrationBean.setOrder(-100); return registrationBean; } + + /** + * IMS请求编码预处理过滤器 + */ + @Bean + public FilterRegistrationBean imsRequestFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new ImsRequestFilter()); + registrationBean.addUrlPatterns("/ims/*"); + registrationBean.setName("imsRequestFilter"); + registrationBean.setOrder(-101); + return registrationBean; + } } diff --git a/src/main/java/com/chinaweal/aiccs/config/ImsRequestFilter.java b/src/main/java/com/chinaweal/aiccs/config/ImsRequestFilter.java new file mode 100644 index 0000000..9e66fa6 --- /dev/null +++ b/src/main/java/com/chinaweal/aiccs/config/ImsRequestFilter.java @@ -0,0 +1,111 @@ +package com.chinaweal.aiccs.config; + +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.regex.Pattern; + +@Slf4j +public class ImsRequestFilter implements Filter { + + // 匹配不可见字符(控制字符0-31和127,以及零宽字符) + private static final Pattern INVISIBLE_CHAR_PATTERN = Pattern.compile( + "[\\x00-\\x1f\\x7f\\u200b-\\u200f\\u2028\\u2029\\ufeff]" + ); + + @Override + public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest request = (HttpServletRequest) req; + + // 只处理 JSON 请求 + String contentType = request.getContentType(); + if (contentType != null && contentType.contains("application/json")) { + CachedBodyHttpServletRequest cachedRequest = + new CachedBodyHttpServletRequest(request); + + String body = cachedRequest.getBody(); + // 移除不可见字符 + String cleanedBody = INVISIBLE_CHAR_PATTERN.matcher(body).replaceAll(""); + + if (!cleanedBody.equals(body)) { + log.warn("ImsRequestFilter removed invisible characters from request body"); + } + + cachedRequest.updateBody(cleanedBody); + chain.doFilter(cachedRequest, resp); + } else { + chain.doFilter(req, resp); + } + } + + private static class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { + private byte[] cachedBody; + + public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { + super(request); + InputStream inputStream = request.getInputStream(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + baos.write(buffer, 0, len); + } + inputStream.close(); + this.cachedBody = baos.toByteArray(); + } + + public String getBody() { + return new String(cachedBody, StandardCharsets.UTF_8); + } + + public void updateBody(String body) { + this.cachedBody = body.getBytes(StandardCharsets.UTF_8); + } + + @Override + public ServletInputStream getInputStream() { + return new CachedBodyServletInputStream(cachedBody); + } + + @Override + public BufferedReader getReader() { + return new BufferedReader( + new InputStreamReader(getInputStream(), StandardCharsets.UTF_8) + ); + } + } + + private static class CachedBodyServletInputStream extends ServletInputStream { + private final ByteArrayInputStream delegate; + + public CachedBodyServletInputStream(byte[] body) { + this.delegate = new ByteArrayInputStream(body); + } + + @Override + public boolean isFinished() { + return delegate.available() == 0; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener listener) { + // non-blocking read not supported for cached body + } + + @Override + public int read() { + return delegate.read(); + } + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0ad3cc5..24d96f5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -79,10 +79,6 @@ spring: multipart: max-file-size: 100MB max-request-size: 100MB - jackson: - parser: - # 允许解析未转义的控制字符(解决你那个中文括号/隐形字符报错) - allow-unquoted-control-chars: true restLog: ignoreServletPath: /druid,/swagger-resources,/v2/api-docs,/webjars,/user/checkSSOLogin