From bce41ce8891a986b2fb396ea6e6f4b533c6dceff Mon Sep 17 00:00:00 2001 From: lihongjie0209 Date: Tue, 5 Dec 2023 14:21:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/lihongjie/coal/common/Constants.java | 1 + .../coal/filter/CacheBodyRequestWrapper.java | 79 +++++++++++++++++++ .../cn/lihongjie/coal/filter/CacheFilter.java | 10 +-- .../cn/lihongjie/coal/filter/SignFilter.java | 68 +++++++++++++--- 4 files changed, 138 insertions(+), 20 deletions(-) create mode 100644 src/main/java/cn/lihongjie/coal/filter/CacheBodyRequestWrapper.java diff --git a/src/main/java/cn/lihongjie/coal/common/Constants.java b/src/main/java/cn/lihongjie/coal/common/Constants.java index db7406d1..6cd377b0 100644 --- a/src/main/java/cn/lihongjie/coal/common/Constants.java +++ b/src/main/java/cn/lihongjie/coal/common/Constants.java @@ -13,6 +13,7 @@ public class Constants { public static final String HTTP_HEADER_TOKEN = "X-Token"; public static final String HTTP_HEADER_CLIENT_SIGN = "X-Client-Sign"; public static final String HTTP_HEADER_CLIENT_RANDOM = "X-Client-Random"; + public static final String HTTP_HEADER_CLIENT_URL = "X-Client-Url"; public static final String HTTP_HEADER_TS = "X-TS"; public static final String RATE_LIMIT_GLOBAL_SESSION_MIN_PREFIX = "global-session-min-rl-"; public static final String RATE_LIMIT_GLOBAL_ANONYMOUS_MIN_PREFIX = "global-anonymous-min-rl-"; diff --git a/src/main/java/cn/lihongjie/coal/filter/CacheBodyRequestWrapper.java b/src/main/java/cn/lihongjie/coal/filter/CacheBodyRequestWrapper.java new file mode 100644 index 00000000..e1a62570 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/filter/CacheBodyRequestWrapper.java @@ -0,0 +1,79 @@ +package cn.lihongjie.coal.filter; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; + +import lombok.SneakyThrows; + +import org.apache.commons.io.IOUtils; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +public class CacheBodyRequestWrapper extends HttpServletRequestWrapper { + + private final CacheBodyServletInputStream inputStream; + + /** + * Constructs a request object wrapping the given request. + * + * @param request the {@link HttpServletRequest} to be wrapped. + * @throws IllegalArgumentException if the request is null + */ + @SneakyThrows + public CacheBodyRequestWrapper(HttpServletRequest request) { + super(request); + inputStream = new CacheBodyServletInputStream(super.getInputStream()); + + } + + @Override + public ServletInputStream getInputStream() throws IOException { + + return inputStream; + } + + public void reset() { + inputStream.reset(); + } + + private class CacheBodyServletInputStream extends ServletInputStream { + private final ServletInputStream stream; + private final ByteArrayInputStream cache; + + @SneakyThrows + public CacheBodyServletInputStream(ServletInputStream stream) { + this.stream = stream; + cache = new ByteArrayInputStream(IOUtils.toByteArray(stream)); + } + + public void reset() { + + cache.reset(); + } + + @Override + public boolean isFinished() { + return cache.available() <= 0; + } + + @Override + public boolean isReady() { + return true; + } + + @SneakyThrows + @Override + public void setReadListener(ReadListener readListener) { + + // readListener.onAllDataRead(); + } + + @Override + public int read() throws IOException { + return cache.read(); + } + } +} diff --git a/src/main/java/cn/lihongjie/coal/filter/CacheFilter.java b/src/main/java/cn/lihongjie/coal/filter/CacheFilter.java index 38e4baae..d8d5110f 100644 --- a/src/main/java/cn/lihongjie/coal/filter/CacheFilter.java +++ b/src/main/java/cn/lihongjie/coal/filter/CacheFilter.java @@ -12,8 +12,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.util.ContentCachingRequestWrapper; -import org.springframework.web.util.ContentCachingResponseWrapper; import java.io.IOException; @@ -29,12 +27,10 @@ public class CacheFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { long start = System.currentTimeMillis(); - var req = new ContentCachingRequestWrapper(request); - var res = new ContentCachingResponseWrapper(response); - filterChain.doFilter(req, res); + var req = new CacheBodyRequestWrapper(request); + filterChain.doFilter(req, response); long end = System.currentTimeMillis(); - res.addHeader(Constants.HTTP_HEADER_RESPONSE_TIME, String.valueOf(end - start)); - res.copyBodyToResponse(); + response.addHeader(Constants.HTTP_HEADER_RESPONSE_TIME, String.valueOf(end - start)); } } diff --git a/src/main/java/cn/lihongjie/coal/filter/SignFilter.java b/src/main/java/cn/lihongjie/coal/filter/SignFilter.java index aa286032..6bad30c9 100644 --- a/src/main/java/cn/lihongjie/coal/filter/SignFilter.java +++ b/src/main/java/cn/lihongjie/coal/filter/SignFilter.java @@ -26,7 +26,9 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; +import org.springframework.util.StopWatch; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @@ -50,8 +52,7 @@ public class SignFilter extends OncePerRequestFilter { @Autowired IpQueryService ipQueryService; @Autowired LoginUserService loginUserService; - @Autowired - RedisTemplate redisTemplate; + @Autowired RedisTemplate redisTemplate; @Override protected void doFilterInternal( @@ -59,6 +60,9 @@ public class SignFilter extends OncePerRequestFilter { throws ServletException, IOException { if (sysConfigService.isEnable(Constants.SYSCONFIG_ENABLE_REQUEST_SIGN)) { + StopWatch stopWatch = new StopWatch(); + + stopWatch.start("基本请求头"); StringBuilder sb = new StringBuilder(); @@ -68,7 +72,11 @@ public class SignFilter extends OncePerRequestFilter { sb.append(StringUtils.defaultString("\n")); // 请求地址 - sb.append(StringUtils.defaultString(request.getRequestURI())); + sb.append( + StringUtils.defaultString( + ObjectUtils.defaultIfNull( + request.getHeader(Constants.HTTP_HEADER_CLIENT_URL), + request.getRequestURI()))); sb.append(StringUtils.defaultString("\n")); // 请求参数 @@ -128,17 +136,40 @@ public class SignFilter extends OncePerRequestFilter { sb.append(StringUtils.defaultString("\n")); - String sha256Hex = DigestUtils.sha256Hex(request.getInputStream()).toUpperCase(); + stopWatch.stop(); + + stopWatch.start("sha256Hex"); + + String sha256Hex; + MediaType mediaType = + MediaType.parseMediaType( + ObjectUtils.defaultIfNull( + request.getContentType(), + MediaType.APPLICATION_OCTET_STREAM_VALUE)); + // 文件上传不对body进行签名 + if (mediaType.isCompatibleWith(MediaType.MULTIPART_FORM_DATA)) { + + sha256Hex = DigestUtils.sha256Hex("").toUpperCase(); + + } else { + sha256Hex = DigestUtils.sha256Hex(request.getInputStream()).toUpperCase(); + } sb.append(StringUtils.defaultString(sha256Hex)); sb.append(StringUtils.defaultString("\n")); + stopWatch.stop(); + + stopWatch.start("签名"); + Mac mac = HmacUtils.getInitializedMac( HmacAlgorithms.HMAC_SHA_256, token.getBytes(StandardCharsets.UTF_8)); mac.update(sb.toString().getBytes(StandardCharsets.UTF_8)); String sign = Hex.encodeHexString(mac.doFinal()).toUpperCase(); + stopWatch.stop(); + String clientSign = request.getHeader(Constants.HTTP_HEADER_CLIENT_SIGN); if (!sign.equals(clientSign)) { @@ -150,22 +181,33 @@ public class SignFilter extends OncePerRequestFilter { return; } - String cr = redisTemplate.opsForValue().get(Constants.CACHE_CLIENT_RANDOM_PREFIX + random); + stopWatch.start("重复请求"); + String cr = + redisTemplate.opsForValue().get(Constants.CACHE_CLIENT_RANDOM_PREFIX + random); - if (StringUtils.equals(cr, "1")){ + if (StringUtils.equals(cr, "1")) { sysLogService.saveSysLog(request, "签名模块", "重复请求", ""); - RequestUtils.writeResponse( - new BizException("请求已过期, 请刷新页面重试"), response); + RequestUtils.writeResponse(new BizException("请求已过期, 请刷新页面重试"), response); return; - }else { - redisTemplate.opsForValue().set(Constants.CACHE_CLIENT_RANDOM_PREFIX + random, "1", TIME_DIFF_MS, TimeUnit.MILLISECONDS); + } else { + redisTemplate + .opsForValue() + .set( + Constants.CACHE_CLIENT_RANDOM_PREFIX + random, + "1", + TIME_DIFF_MS, + TimeUnit.MILLISECONDS); } + stopWatch.stop(); + + if (request instanceof CacheBodyRequestWrapper cacheBodyRequestWrapper) { + cacheBodyRequestWrapper.reset(); + } + + log.info("签名验证通过: {}", stopWatch.prettyPrint()); } doFilter(request, response, filterChain); } - - - }