zhouxiang
2023-02-22 9b2704d1954ac51320da4c8060d5aa4cf97134a6
提交 | 用户 | age
9ec338 1 package com.hx.phip.interceptor;
F 2
3 import com.alibaba.fastjson.JSONObject;
4 import com.hx.common.BaseController;
5 import com.hx.exception.TipsException;
da9ea8 6 import com.hx.phiappt.model.meiji.MjRequestLog;
9ec338 7 import com.hx.phip.config.GlobalExceptionHandler;
da9ea8 8 import com.hx.phip.service.meiji.MjRequestLogService;
9ec338 9 import com.hx.redis.RedisUtil;
F 10 import com.hx.util.HttpServletRequestUtil;
11 import com.hx.util.StringUtils;
12 import com.platform.resultTool.PlatformCode;
13 import lombok.SneakyThrows;
14 import org.aspectj.lang.ProceedingJoinPoint;
15 import org.aspectj.lang.annotation.Around;
16 import org.aspectj.lang.annotation.Aspect;
17 import org.aspectj.lang.annotation.Pointcut;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20 import org.springframework.http.MediaType;
21 import org.springframework.stereotype.Component;
22 import org.springframework.web.context.request.RequestAttributes;
23 import org.springframework.web.context.request.RequestContextHolder;
24 import org.springframework.web.context.request.ServletRequestAttributes;
25 import sun.misc.BASE64Decoder;
26
27 import javax.annotation.Resource;
28 import javax.servlet.http.HttpServletRequest;
29 import java.io.BufferedReader;
d66f76 30 import java.io.UnsupportedEncodingException;
F 31 import java.net.URLDecoder;
9ec338 32 import java.net.URLEncoder;
F 33 import java.security.KeyFactory;
34 import java.security.PublicKey;
35 import java.security.Signature;
36 import java.security.spec.X509EncodedKeySpec;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.Map;
d66f76 41 import java.util.regex.Matcher;
F 42 import java.util.regex.Pattern;
9ec338 43
F 44 /**
45  * 美际小肤 外部接口拦截器
46  *
47  * @USER: fhx
48  * @DATE: 2023/1/6
49  **/
50 @Aspect
51 @Component
52 public class MeiJiInterceptor extends BaseController {
53
da9ea8 54     @Resource
F 55     private MjRequestLogService mjRequestLogService;
56
9ec338 57     //机构公钥
F 58     private static String jgPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7zFeslbt8BQ3FodPJXKILNQlZiLxmy0VJ6pWbcN/0UxNqoUMTw4IsGdCvJwlyRntL9WeGvrol9XirPH0dMAoua1xM9CkaTXI4dZnpoGVDsJxwQUi0Ld76pnpY8JfcKUZYCc4gGjqonzdIWK5DbK8VRzS2VsxgXG6yHv/HxyYNTQIDAQAB";
59     //加密算法
60     public static final String KEY_ALGORITHM = "RSA";
61     //签名算法
62     public static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";
63
64     private static Logger logger = LoggerFactory.getLogger(MeiJiInterceptor.class.getName());
65
66     /**
67      * 设置拦截的位置
68      * 拦截要校验的包
69      */
70     @Pointcut("within(com.hx.phip.meiji..*))")
71     public void checkSign() {
72         System.out.println("拦截啦");
73     }
74
75     /*   *设置拦截的位置的操作
76      * @param joinPoint
77      * @return
78      * @throws Throwable*/
79
80     @Around("checkSign()")
81     public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
82         Object result = "SUCCESS";
da9ea8 83         //获取签名
F 84         HttpServletRequest request = getRequest();
85         String method = request.getServletPath();
f1cda7 86         String bodyJson = "";
da9ea8 87         Integer status = MjRequestLog.STATUS_SUC;
F 88         String sign = request.getHeader("sign");
f1cda7 89         String errMsg = null;
9ec338 90         try {
F 91
92             //判断请求类型
93             String contentType = request.getContentType();
94             if (StringUtils.noNull(contentType)) {
95                 contentType = contentType.toLowerCase();
96             }
97
98             if (!MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
99                 throwParamException("请求数据格式非JSON格式!");
100             }
101
da9ea8 102
9ec338 103             if (StringUtils.isEmpty(sign)) {
F 104                 throwParamException("请求签名为空!");
105             }
106
da9ea8 107             String str;
9ec338 108             // 获取参数, 只取自定义的参数, 自带的HttpServletRequest, HttpServletResponse不管
F 109             BufferedReader br = request.getReader();
110             while ((str = br.readLine()) != null) {
da9ea8 111                 bodyJson += str;
9ec338 112             }
F 113
468c00 114
5e6f64 115             JSONObject body = JSONObject.parseObject(unicodeToCN(bodyJson));
da9ea8 116
468c00 117             //如果走的是皮肤报告接收接口,则参与验证签名的参数只是部分参数
F 118             if(method.equals("/meiji/api/report/receive")){
119                 //清除不参与验签的参数
120                 body.remove("created_time");
121                 body.remove("icon");
122                 body.remove("share_url");
123                 body.remove("resources");
124                 body.remove("contents");
f8571e 125                 body.remove("view_url");
468c00 126             }
F 127
9ec338 128             //验签
2ed344 129             verify(splicingParams(body), jgPublicKey, sign);
9ec338 130
F 131             result = joinPoint.proceed();
132         } catch (Exception e) {
133             logger.error("美际拦截器异常!");
134             logger.error(GlobalExceptionHandler.getExceptionInformation(e));
f1cda7 135             errMsg = e.getMessage().length() > 255 ? e.getMessage().substring(0, 250) : e.getMessage();
9ec338 136             JSONObject data = new JSONObject();
F 137             data.put("code", PlatformCode.ERROR_SYSTEM);
f1cda7 138             data.put("message", errMsg);
9ec338 139             result = data;
da9ea8 140             status = MjRequestLog.STATUS_FAIL;
9ec338 141         } finally {
da9ea8 142             //新增请求日志
178a3f 143             mjRequestLogService.insert(new MjRequestLog(method, sign, unicodeToCN(bodyJson), errMsg, status));
9ec338 144             return result;
F 145         }
146
147     }
d66f76 148
f8571e 149     public static void main(String args[]) throws Exception {
F 150         String s = "{\"channel_id\":\"25853\",\"store_id\":\"007\",\"customer_id\":\"ac0adba7adab11ed91bf0c42a12ee9a2\",\"report_id\":\"25397711679918081\",\"scanner_id\":\"MEIJIXIAOFUQA001\",\"created_time\":1676969163,\"icon\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/icon.jpg\",\"view_url\":\"http:\\/\\/skin-view.myreal3d.com\\/skinPreview?scope=OPEN:basic&channel_id=25853&store_id=007&openid=ac0adba7adab11ed91bf0c42a12ee9a2&report_id=25397711679918081\",\"share_url\":\"http:\\/\\/dr2.myreal3d.com\\/skinReport?hash=EaB7sgdrZzVKVGl0&enable_history=1\",\"resources\":[{\"type\":\"zip\",\"flag\":\"obj\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/obj.zip\"},{\"type\":\"zip\",\"flag\":\"thumb\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/thumb.zip\"},{\"type\":\"zip\",\"flag\":\"mask\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/mask.zip\"},{\"type\":\"jpg\",\"flag\":\"rgb_cool\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Rgb_Cool.jpg\"},{\"type\":\"jpg\",\"flag\":\"rgb\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Rgb.jpg\"},{\"type\":\"jpg\",\"flag\":\"brown_face\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Brown_Face.jpg\"},{\"type\":\"jpg\",\"flag\":\"red\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Red.jpg\"},{\"type\":\"jpg\",\"flag\":\"red2_ex_face\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Red2_Ex_Face.jpg\"},{\"type\":\"jpg\",\"flag\":\"red2_face\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Red2_Face.jpg\"},{\"type\":\"jpg\",\"flag\":\"uv\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Uv.jpg\"},{\"type\":\"jpg\",\"flag\":\"uv_ex_face\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Uv_Ex_Face.jpg\"},{\"type\":\"jpg\",\"flag\":\"plh\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Plh.jpg\"},{\"type\":\"jpg\",\"flag\":\"plv\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Plv.jpg\"},{\"type\":\"jpg\",\"flag\":\"red2_heat\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Red2_Heat_Face.jpg\"},{\"type\":\"jpg\",\"flag\":\"brown_heat\",\"url\":\"https:\\/\\/external-storage-1251693531.cos.ap-shanghai.myqcloud.com\\/xiaofu\\/uploads\\/2023-02-21\\/25397711679918081\\/Brown_Heat_Face.jpg\"}],\"contents\":[{\"name\":\"抬头纹\",\"degree\":\"明显\",\"level\":2,\"result\":2.61},{\"name\":\"鱼尾纹\",\"degree\":\"中度\",\"level\":2,\"result\":2.41},{\"name\":\"眉间纹\",\"degree\":\"轻微\",\"level\":1,\"result\":4.1},{\"name\":\"痤疮\",\"degree\":\"Ⅰ级\",\"level\":1,\"result\":4.5},{\"name\":\"毛孔\",\"degree\":\"较粗大\",\"level\":1,\"result\":4.12},{\"name\":\"红血丝\",\"degree\":\"重度\",\"level\":3,\"result\":1.93},{\"name\":\"黑头\",\"degree\":\"无\",\"level\":0,\"result\":4.75},{\"name\":\"肤色不均\",\"degree\":\"中度\",\"level\":2,\"result\":2.37},{\"name\":\"眼下皱纹\",\"degree\":\"中度\",\"level\":2,\"result\":2.62},{\"name\":\"色斑\",\"degree\":\"中度\",\"level\":2,\"result\":3.44},{\"name\":\"泛红\",\"degree\":\"轻度\",\"level\":1,\"result\":3.6},{\"name\":\"T区出油\",\"degree\":\"无\",\"level\":0,\"result\":4.59},{\"name\":\"U区出油\",\"degree\":\"轻度\",\"level\":1,\"result\":4.11}]}";
151         JSONObject body = JSONObject.parseObject(unicodeToCN(s));
152         body.remove("created_time");
153         body.remove("icon");
154         body.remove("share_url");
155         body.remove("resources");
156         body.remove("contents");
157         body.remove("view_url");
158         String sign = "Wb1ZO6v+2ej9eRfs38mUs2dD4Yu7gJJncJHnbRSTDHj5WIK2uQWTe7Berz+heEfvFL/0j8Qx5jezcJoTgoM0hecYA09hKGSDwt2QeKt9BVchcNHvFDC+Aj56bqtB+/nZFbuY8+bfUTIL4xhFxVYznoV3ygY1Ktq2UEt+OY/uAgc=";
159         verify(splicingParams(body), jgPublicKey, sign);
d66f76 160     }
F 161
9ec338 162
da9ea8 163     ////////////////////////////////////////////////////////////////
F 164
9ec338 165
F 166     /**
167      * 拼接参数
168      *
169      * @param params 参数
170      */
171     @SneakyThrows
172     public static String splicingParams(Map<String, Object> params) {
173         List<String> keys = new ArrayList<>(params.keySet());
174         Collections.sort(keys);
175         String prestr = "";
176         for (int i = 0; i < keys.size(); i++) {
177             String key = keys.get(i);
178             String value = params.get(key).toString();
3a2e4f 179             //对中文转码字符转为中文
F 180 //            value = unicodeToCN(value);
9ec338 181             if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
F 182                 prestr = prestr + key + "=" + value;
183             } else {
184                 prestr = prestr + key + "=" + value + "&";
185             }
186         }
187         return prestr;
188     }
189
190
191     /**
192      * 验签
193      *
194      * @param requestData 数据
195      * @param publicKey   公钥
196      * @param sign        数字签名
197      * @return
198      * @throws Exception
199      */
200     public static void verify(String requestData, String publicKey, String sign) throws Exception {
201
202         if (StringUtils.isEmpty(publicKey) || StringUtils.isEmpty(sign)) {
203             throw new TipsException("参数异常!");
204         }
205
206         //解密公钥
207         byte[] keyBytes = new BASE64Decoder().decodeBuffer(publicKey);
208         //构造X509EncodedKeySpec对象
209         X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
210         //指定加密算法
211         KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
212         //取公钥匙对象
213         PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);
214
215         Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
216         signature.initVerify(publicKey2);
217         signature.update(requestData.getBytes());
218         //验证签名是否正常
219         try {
468c00 220             if(signature.verify(new BASE64Decoder().decodeBuffer(sign))){
F 221                 logger.info("==美际 验证签名成功==");
222             }else {
223                 throw new TipsException("== 美际 验签失败!==");
224             }
9ec338 225         } catch (Exception e) {
468c00 226             throw new TipsException(e.getMessage());
9ec338 227         }
F 228     }
229
d66f76 230     /**
F 231      * Unicode转 汉字字符串
232      *
233      * @param str
234      * @return
235      */
236     public static String unicodeToCN(String str) {
237         if(StringUtils.isEmpty(str)){
238             str = "";
239         }
240         Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
241         Matcher matcher = pattern.matcher(str);
242         char ch;
243         while (matcher.find()) {
244             ch = (char) Integer.parseInt(matcher.group(2), 16);
245             str = str.replace(matcher.group(1), ch + "");
246         }
247         return str;
248     }
249
250     /**
251      * 中文转Unicode
252      * 其他英文字母或特殊符号也可进行Unicode编码
253      * @param cn
254      * @return
255      */
256     public static String cnToUnicode(String cn) {
257         char[] chars = cn.toCharArray();
258         StringBuilder returnStr = new StringBuilder();
259         for (int i = 0; i < chars.length; i++) {
260             returnStr.append("\\u").append(Integer.toString(chars[i], 16));
261         }
262         return returnStr.toString();
263     }
264
9ec338 265 }