ApiSignInterceptor.java 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package com.smppw.analysis.infrastructure.components;
  2. import cn.hutool.core.util.StrUtil;
  3. import cn.hutool.crypto.SignUtil;
  4. import cn.hutool.json.JSONUtil;
  5. import com.smppw.analysis.infrastructure.config.AnalysisProperty;
  6. import com.smppw.common.pojo.ResultVo;
  7. import jakarta.servlet.http.HttpServletRequest;
  8. import jakarta.servlet.http.HttpServletResponse;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11. import org.springframework.http.MediaType;
  12. import org.springframework.lang.NonNull;
  13. import org.springframework.web.servlet.HandlerInterceptor;
  14. import java.nio.charset.Charset;
  15. import java.time.Duration;
  16. import java.util.Map;
  17. /**
  18. * @author wangzaijun
  19. * @date 2023/8/7 15:58
  20. * @description api验证签名拦截器
  21. */
  22. public class ApiSignInterceptor implements HandlerInterceptor {
  23. private static final long MAX_TIMEOUT = 50000L;
  24. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  25. private final AnalysisProperty property;
  26. public ApiSignInterceptor(AnalysisProperty property) {
  27. this.property = property;
  28. }
  29. @Override
  30. public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
  31. if (logger.isInfoEnabled()) {
  32. logger.info(StrUtil.format("{} 接口正在被 {} 请求!", request.getRequestURI(), request.getRemoteAddr()));
  33. }
  34. if (!this.property.getEnableSign()) {
  35. if (logger.isDebugEnabled()) {
  36. logger.debug("接口不需要签名验证!");
  37. }
  38. return HandlerInterceptor.super.preHandle(request, response, handler);
  39. }
  40. String sign = request.getHeader("sign");
  41. if (StrUtil.isBlank(sign)) {
  42. this.writeFailJson(response, "sign 参数为空");
  43. return false;
  44. }
  45. String timestamp = request.getHeader("timestamp");
  46. if (StrUtil.isBlank(timestamp)) {
  47. this.writeFailJson(response, "timestamp 参数为空");
  48. return false;
  49. }
  50. String appKey = request.getParameter("appKey");
  51. AnalysisProperty.AppSign appSign = this.property.getAppSigns().stream()
  52. .filter(e -> appKey.equals(e.getAppKey())).findFirst().orElse(null);
  53. if (appSign == null) {
  54. this.writeFailJson(response, "appKey非法");
  55. return false;
  56. }
  57. if (!appSign.getEnabled()) {
  58. logger.warn(StrUtil.format("{} 应用未开启接口签名验证!", appKey));
  59. return HandlerInterceptor.super.preHandle(request, response, handler);
  60. }
  61. boolean flag = this.checkTimestamp(request, response, timestamp, appSign.getTimeout());
  62. if (!flag) {
  63. return false;
  64. }
  65. String appSecret = appSign.getAppSecret();
  66. Map<String, String[]> parameterMap = request.getParameterMap();
  67. parameterMap.entrySet().removeIf(next -> "appKey".equalsIgnoreCase(next.getKey()));
  68. String paramsSign = SignUtil.signParamsMd5(parameterMap, appSecret);
  69. if (!sign.equalsIgnoreCase(paramsSign)) {
  70. this.writeFailJson(response, "签名验证失败");
  71. return false;
  72. }
  73. return HandlerInterceptor.super.preHandle(request, response, handler);
  74. }
  75. private boolean checkTimestamp(HttpServletRequest request, HttpServletResponse response, String timestamp, Duration timeout) throws Exception {
  76. long millis = System.currentTimeMillis();
  77. long time = Long.parseLong(timestamp);
  78. long seconds = Math.min(timeout.getSeconds(), MAX_TIMEOUT);
  79. if (millis - time > seconds) {
  80. logger.error(StrUtil.format("接口有重放攻击,调用方:{}-{}"), request.getRemoteUser(), request.getRemoteAddr());
  81. this.writeFailJson(response, "请求失败!");
  82. return false;
  83. }
  84. return true;
  85. }
  86. private synchronized void writeFailJson(HttpServletResponse response, String msg) throws Exception {
  87. ResultVo<?> resultVo = ResultVo.fail(400, msg);
  88. response.setContentType(MediaType.APPLICATION_JSON_VALUE);
  89. response.setCharacterEncoding(Charset.defaultCharset().displayName());
  90. response.getWriter().write(JSONUtil.toJsonStr(resultVo));
  91. response.getWriter().flush();
  92. }
  93. }