mirror of
https://codeup.aliyun.com/64f7d6b8ce01efaafef1e678/coal/coal.git
synced 2026-01-25 07:46:40 +08:00
完善签名
This commit is contained in:
@@ -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-";
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, String> redisTemplate;
|
||||
@Autowired RedisTemplate<String, String> 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user