From 8cbf1681db68c2719259f6125943dbf5f079d32e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Mon, 7 Apr 2025 09:17:42 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=E6=8E=A5=E5=8F=A3=E9=AA=8C=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cool/store/utils/OpenSignatureUtil.java | 75 +++++++++++++ .../store/config/OpenApiValidateFilter.java | 105 ++++++++++++++++++ .../cool/store/config/SignValidateFilter.java | 2 +- .../store/config/TokenValidateFilter.java | 2 +- .../controller/webb/OpenApiController.java | 24 ++++ 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java create mode 100644 coolstore-partner-web/src/main/java/com/cool/store/config/OpenApiValidateFilter.java create mode 100644 coolstore-partner-web/src/main/java/com/cool/store/controller/webb/OpenApiController.java diff --git a/coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java b/coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java new file mode 100644 index 000000000..03f970669 --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/utils/OpenSignatureUtil.java @@ -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 params, String appSecret) { + // 1. 分离固定参数和业务参数 + String appKey = params.get("appKey"); + String timestamp = params.get("timestamp"); + + // 2. 创建不包含固定参数的临时Map用于排序 + Map 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(); + } +} \ No newline at end of file diff --git a/coolstore-partner-web/src/main/java/com/cool/store/config/OpenApiValidateFilter.java b/coolstore-partner-web/src/main/java/com/cool/store/config/OpenApiValidateFilter.java new file mode 100644 index 000000000..22646d865 --- /dev/null +++ b/coolstore-partner-web/src/main/java/com/cool/store/config/OpenApiValidateFilter.java @@ -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 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() { + } +} \ No newline at end of file diff --git a/coolstore-partner-web/src/main/java/com/cool/store/config/SignValidateFilter.java b/coolstore-partner-web/src/main/java/com/cool/store/config/SignValidateFilter.java index bcb8fcd87..d96af4e4a 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/config/SignValidateFilter.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/config/SignValidateFilter.java @@ -89,7 +89,7 @@ public class SignValidateFilter implements Filter { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; String uri = request.getRequestURI(); - if(uri.startsWith("/zxjp/pc")){ + if(uri.startsWith("/zxjp/pc")||uri.startsWith("/zxjp/open")){ filterChain.doFilter(servletRequest, servletResponse); return; } diff --git a/coolstore-partner-web/src/main/java/com/cool/store/config/TokenValidateFilter.java b/coolstore-partner-web/src/main/java/com/cool/store/config/TokenValidateFilter.java index e84cb6e3e..2115f4f26 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/config/TokenValidateFilter.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/config/TokenValidateFilter.java @@ -82,7 +82,7 @@ public class TokenValidateFilter implements Filter { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest reqs = (HttpServletRequest) servletRequest; String uri = reqs.getRequestURI(); - if(uri.startsWith("/zxjp/mini")){ + if(uri.startsWith("/zxjp/mini")||uri.startsWith("/zxjp/open")){ filterChain.doFilter(servletRequest, servletResponse); return; } diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/OpenApiController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/OpenApiController.java new file mode 100644 index 000000000..200531103 --- /dev/null +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/OpenApiController.java @@ -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 statusRefresh(){ + return ResponseResult.success(Boolean.TRUE); + } + +} From d776982c57af11bdba2f49e4728156c7dbebe293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Mon, 7 Apr 2025 09:24:56 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:PC=E6=9C=BA=E4=BC=9A=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/webb/PCThirdApiController.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCThirdApiController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCThirdApiController.java index 204ace3a6..9dbf3f246 100644 --- a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCThirdApiController.java +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/PCThirdApiController.java @@ -2,6 +2,8 @@ package com.cool.store.controller.webb; import com.cool.store.request.bigdata.ProfitDataRequest; import com.cool.store.request.oppty.CityRequest; +import com.cool.store.request.oppty.FollowRequest; +import com.cool.store.request.oppty.InspectionRequest; import com.cool.store.request.oppty.OpportunityListRequest; import com.cool.store.response.ResponseResult; import com.cool.store.response.bigdata.ProfitDataResponse; @@ -44,4 +46,16 @@ public class PCThirdApiController { // 调用第三方接口 return ResponseResult.success(thirdOpportunityService.cityList(request)); } + + @PostMapping("/followOpportunity") + @ApiOperation("关注/取消关注 机会点") + public ResponseResult followOpportunity(@Valid @RequestBody FollowRequest request) { + return ResponseResult.success(thirdOpportunityService.followOpportunity(request)); + } + + @PostMapping("/inspectionOpportunity") + @ApiOperation("考察机会点") + public ResponseResult inspectionOpportunity(@Valid @RequestBody InspectionRequest request) { + return ResponseResult.success(thirdOpportunityService.inspectionOpportunity(request)); + } } From c431754f28f6bcc077aed4bc0faab06e9001da1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E7=AB=B9=E7=BA=A2?= Date: Mon, 7 Apr 2025 10:08:56 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:PC=E6=9C=BA=E4=BC=9A=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-test.properties | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coolstore-partner-web/src/main/resources/application-test.properties b/coolstore-partner-web/src/main/resources/application-test.properties index b80c82d93..0bfae2edd 100644 --- a/coolstore-partner-web/src/main/resources/application-test.properties +++ b/coolstore-partner-web/src/main/resources/application-test.properties @@ -96,3 +96,6 @@ zx.big.data.appSecret=35b8b9a400b4430fa022190be0913cd6 api.auth.url=https://api.zhengxindzg.cn api.auth.username=GkqgAhUJ7p9swJo api.auth.secret=NzVrnS3OWiupdDY + +cool.api.appKey=123 +cool.api.secret=123