feat:接口验签
This commit is contained in:
@@ -0,0 +1,75 @@
|
|||||||
|
package com.cool.store.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author su'zh
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class OpenSignatureUtil {
|
||||||
|
private static final String HMAC_SHA256 = "HmacSHA256";
|
||||||
|
|
||||||
|
public static String generateSign(Map<String, String> params, String appSecret) {
|
||||||
|
// 1. 分离固定参数和业务参数
|
||||||
|
String appKey = params.get("appKey");
|
||||||
|
String timestamp = params.get("timestamp");
|
||||||
|
|
||||||
|
// 2. 创建不包含固定参数的临时Map用于排序
|
||||||
|
Map<String, String> sortedParams = new TreeMap<>(
|
||||||
|
params.entrySet().stream()
|
||||||
|
.filter(e -> !"appkey".equals(e.getKey()))
|
||||||
|
.filter(e -> !"timestamp".equals(e.getKey()))
|
||||||
|
.filter(e -> !"sign".equals(e.getKey()))
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
Map.Entry::getKey,
|
||||||
|
Map.Entry::getValue
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. 构建参数字符串:业务参数(排序后) + 固定参数
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
// 3.1 添加排序后的业务参数
|
||||||
|
sortedParams.forEach((key, value) -> {
|
||||||
|
sb.append(key).append("=").append(value).append("&");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3.2 添加固定参数(不参与排序)
|
||||||
|
sb.append("appkey=").append(appKey)
|
||||||
|
.append("×tamp=").append(timestamp);
|
||||||
|
|
||||||
|
// 4. 生成签名
|
||||||
|
return hmacSha256(sb.toString(), appSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String hmacSha256(String data, String key) {
|
||||||
|
try {
|
||||||
|
Mac sha256_HMAC = Mac.getInstance(HMAC_SHA256);
|
||||||
|
SecretKeySpec secret_key = new SecretKeySpec(
|
||||||
|
key.getBytes(StandardCharsets.UTF_8),
|
||||||
|
HMAC_SHA256
|
||||||
|
);
|
||||||
|
sha256_HMAC.init(secret_key);
|
||||||
|
byte[] hash = sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return bytesToHex(hash);
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||||
|
throw new RuntimeException("生成签名失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String bytesToHex(byte[] bytes) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (byte b : bytes) {
|
||||||
|
result.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package com.cool.store.config;
|
||||||
|
import com.cool.store.constants.CommonConstants;
|
||||||
|
import com.cool.store.enums.ErrorCodeEnum;
|
||||||
|
import com.cool.store.exception.ServiceException;
|
||||||
|
import com.cool.store.utils.OpenSignatureUtil;
|
||||||
|
import com.cool.store.utils.UUIDUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author suzhuhong
|
||||||
|
* @Date 2025/4/5 18:11
|
||||||
|
* @Version 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Order(3)
|
||||||
|
@Slf4j
|
||||||
|
public class OpenApiValidateFilter implements Filter {
|
||||||
|
|
||||||
|
@Value("${cool.api.appKey}")
|
||||||
|
private String coolAppKey;
|
||||||
|
@Value("${cool.api.secret}")
|
||||||
|
private String coolAppSecret;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
|
||||||
|
MDC.put(CommonConstants.REQUEST_ID, UUIDUtils.get32UUID());
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
if(!uri.startsWith("/zxjp/open")){
|
||||||
|
filterChain.doFilter(servletRequest, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 验证时间戳
|
||||||
|
try {
|
||||||
|
String timestampStr = request.getParameter("timestamp");
|
||||||
|
if (timestampStr == null) {
|
||||||
|
log.info("timestampStr is null {}","缺少timestamp参数");
|
||||||
|
throw new ServiceException(ErrorCodeEnum.SIGN_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
long timestamp = Long.parseLong(timestampStr)/1000;
|
||||||
|
long currentTime = System.currentTimeMillis()/1000;
|
||||||
|
long timeDiff = Math.abs(currentTime - timestamp);
|
||||||
|
|
||||||
|
if (timeDiff > 300) {
|
||||||
|
log.info("OpenApiValidateFilter==>{}","请求已过期,服务器时间:" + currentTime + " 请求时间:" + timestamp);
|
||||||
|
throw new ServiceException(ErrorCodeEnum.SIGN_FAIL);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.info("OpenApiValidateFilter==>{}","非法timestamp格式");
|
||||||
|
throw new ServiceException(ErrorCodeEnum.SIGN_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 验证签名
|
||||||
|
String appKey = request.getParameter("appKey");
|
||||||
|
if (appKey == null || !coolAppKey.equals(appKey)) {
|
||||||
|
log.info("OpenApiValidateFilter==>{}","无效的appKey");
|
||||||
|
throw new ServiceException(ErrorCodeEnum.SIGN_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
String clientSign = request.getParameter("sign");
|
||||||
|
if (clientSign == null) {
|
||||||
|
throw new ServiceException(ErrorCodeEnum.SIGN_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有请求参数
|
||||||
|
Map<String, String> params = request.getParameterMap().entrySet().stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
Map.Entry::getKey,
|
||||||
|
e -> String.join(",", e.getValue())));
|
||||||
|
|
||||||
|
String serverSign = OpenSignatureUtil.generateSign(params, coolAppSecret);
|
||||||
|
|
||||||
|
if (!serverSign.equalsIgnoreCase(clientSign)) {
|
||||||
|
throw new ServiceException(ErrorCodeEnum.SIGN_FAIL);
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
} finally {
|
||||||
|
MDC.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -89,7 +89,7 @@ public class SignValidateFilter implements Filter {
|
|||||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
String uri = request.getRequestURI();
|
String uri = request.getRequestURI();
|
||||||
if(uri.startsWith("/zxjp/pc")){
|
if(uri.startsWith("/zxjp/pc")||uri.startsWith("/zxjp/open")){
|
||||||
filterChain.doFilter(servletRequest, servletResponse);
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class TokenValidateFilter implements Filter {
|
|||||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
HttpServletRequest reqs = (HttpServletRequest) servletRequest;
|
HttpServletRequest reqs = (HttpServletRequest) servletRequest;
|
||||||
String uri = reqs.getRequestURI();
|
String uri = reqs.getRequestURI();
|
||||||
if(uri.startsWith("/zxjp/mini")){
|
if(uri.startsWith("/zxjp/mini")||uri.startsWith("/zxjp/open")){
|
||||||
filterChain.doFilter(servletRequest, servletResponse);
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.cool.store.controller.webb;
|
||||||
|
|
||||||
|
import com.cool.store.response.ResponseResult;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author suzhuhong
|
||||||
|
* @Date 2025/4/5 18:19
|
||||||
|
* @Version 1.0
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/open/v1/")
|
||||||
|
@Api(tags = "对外接口")
|
||||||
|
public class OpenApiController {
|
||||||
|
|
||||||
|
@GetMapping("/statusRefresh")
|
||||||
|
public ResponseResult<Boolean> statusRefresh(){
|
||||||
|
return ResponseResult.success(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user