diff --git a/snowy-common/src/main/java/vip/xiaonuo/common/filter/SignatureInterceptor.java b/snowy-common/src/main/java/vip/xiaonuo/common/filter/SignatureInterceptor.java new file mode 100644 index 00000000..36dc11a4 --- /dev/null +++ b/snowy-common/src/main/java/vip/xiaonuo/common/filter/SignatureInterceptor.java @@ -0,0 +1,113 @@ +package vip.xiaonuo.common.filter; + +import cn.hutool.extra.spring.SpringUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.env.Environment; +import org.springframework.web.servlet.HandlerInterceptor; +import vip.xiaonuo.common.exception.CommonException; + +import java.util.Comparator; +import java.util.Map; +import java.util.TreeMap; + +/** + * 签名过滤器 + */ +public class SignatureInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { + + /*从header中读取appSign*/ + String sign = httpServletRequest.getHeader("appSign"); + if (StringUtils.isEmpty(sign)) { + throw new CommonException("没有签名信息!", 40052050); + } + + String appTime = httpServletRequest.getParameter("appTime"); + if (StringUtils.isEmpty(appTime)) { + throw new CommonException("请在Parameter中传入时间戳(毫秒级)!", 40052051); + } + + Map bodyData = SignatureInterceptor.sortMapByKey(httpServletRequest.getParameterMap()); + + if (bodyData == null) { + throw new CommonException("没有请求参数!", 40052052); + } + + String original = SignatureInterceptor.transformationUri(bodyData); + + if (StringUtils.isEmpty(sign) || !this.isMatching(sign, original)) { + throw new CommonException("应用签名验证不通过!", 40024053); + } + + return true; + } + + public static String makeSign(String appId, String appKey, String body) { + String sha1 = DigestUtils.sha1Hex(appKey + body + appKey); + return DigestUtils.md5Hex(sha1 + appId); + } + + public static Map sortMapByKey(Map map) { + if (map == null || map.isEmpty()) { + return null; + } + Map sortMap = new TreeMap<>(Comparator.naturalOrder()); + sortMap.putAll(map); + return sortMap; + } + + public void parseBodySign(String sign, Map body) throws CommonException { + + } + + public Boolean isMatching(String sign, String original) { + Environment environment = SpringUtil.getBean(Environment.class); + String securityAccessKey = environment.getProperty("spring.enjoy-buy.security-access-key"); + String securityAccessSecret = environment.getProperty("spring.enjoy-buy.security-access-secret"); + String upSign = this.makeSign(securityAccessKey, securityAccessSecret, original); + + return sign.equalsIgnoreCase(upSign); + + } + + public static String transformationUri(Map map) { + + StringBuilder stringBuilder = new StringBuilder(); + + for (String key : map.keySet()) { + Object value = map.get(key); + if ("appSign".equalsIgnoreCase(key) || value == null || StringUtils.isEmpty(value.toString())) { + continue; + } + + stringBuilder.append(key); + stringBuilder.append("="); + if (map.get(key).getClass().isArray()) { + Object[] objects = (Object[]) map.get(key); + StringBuilder valueBuilder = new StringBuilder(); + for (Object o : objects) { + valueBuilder.append(o); + valueBuilder.append(","); + } + valueBuilder.deleteCharAt(valueBuilder.length() - 1); + stringBuilder.append(valueBuilder); + } else { + stringBuilder.append(map.get(key)); + } + stringBuilder.append("&"); + } + + if (stringBuilder.length() > 0) { + stringBuilder.deleteCharAt(stringBuilder.length() - 1); + } + + return stringBuilder.toString(); + } + + +} diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/config/AuthConfigure.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/config/AuthConfigure.java index 1f471153..905475a1 100644 --- a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/config/AuthConfigure.java +++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/config/AuthConfigure.java @@ -28,6 +28,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import vip.xiaonuo.auth.core.enums.SaClientTypeEnum; import vip.xiaonuo.auth.core.util.StpClientLoginUserUtil; import vip.xiaonuo.auth.core.util.StpLoginUserUtil; +import vip.xiaonuo.common.filter.SignatureInterceptor; import java.util.List; @@ -56,6 +57,10 @@ public class AuthConfigure implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { // 注册注解拦截器,并排除不需要注解鉴权的接口地址 (与登录拦截器无关,只是说明哪些接口不需要被拦截器拦截,此处都拦截) registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**"); + registry.addInterceptor(new SignatureInterceptor()) + .addPathPatterns("/**") + /*排除文档相关地址*/ + .excludePathPatterns("/doc**", "/webjars/**", "/v3/api-docs/**"); } @Primary diff --git a/snowy-web-app/src/main/resources/application.properties b/snowy-web-app/src/main/resources/application.properties index ca14f670..ea379f41 100644 --- a/snowy-web-app/src/main/resources/application.properties +++ b/snowy-web-app/src/main/resources/application.properties @@ -27,9 +27,9 @@ spring.servlet.multipart.max-file-size=100MB # mysql spring.datasource.dynamic.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/snowy?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true -spring.datasource.dynamic.datasource.master.username=root -spring.datasource.dynamic.datasource.master.password=123456 +spring.datasource.dynamic.datasource.master.url=jdbc:mysql://10.10.10.7:3306/membershipsystem?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useInformationSchema=true +spring.datasource.dynamic.datasource.master.username=MembershipSystem +spring.datasource.dynamic.datasource.master.password=ZtCMBHbjwanJWxbm spring.datasource.dynamic.strict=true # postgres @@ -100,9 +100,9 @@ spring.jackson.serialization.write-dates-as-timestamps=true # redis configuration ######################################### spring.data.redis.database=1 -spring.data.redis.host=127.0.0.1 +spring.data.redis.host=10.10.10.3 spring.data.redis.port=6379 -spring.data.redis.password= +spring.data.redis.password=121121Jie spring.data.redis.timeout=10s spring.data.redis.lettuce.pool.max-active=200 @@ -201,3 +201,6 @@ springdoc.group-configs[6].packages-to-scan=vip.xiaonuo.sys # common configuration snowy.config.common.front-url=http://localhost:81 snowy.config.common.backend-url=http://localhost:82 + +spring.enjoy-buy.security-access-key=wxd504412d92c5ffe7 +spring.enjoy-buy.security-access-secret=0c7bf34850bb54158d261e5914e807ee \ No newline at end of file