package com.hx.phip.interceptor; import com.alibaba.fastjson.JSONObject; import com.hx.common.BaseController; import com.hx.exception.TipsException; import com.hx.phiappt.model.meiji.MjRequestLog; import com.hx.phip.config.GlobalExceptionHandler; import com.hx.phip.service.meiji.MjRequestLogService; import com.hx.redis.RedisUtil; import com.hx.util.HttpServletRequestUtil; import com.hx.util.StringUtils; import com.platform.resultTool.PlatformCode; import lombok.SneakyThrows; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import sun.misc.BASE64Decoder; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.KeyFactory; import java.security.PublicKey; import java.security.Signature; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 美际小肤 外部接口拦截器 * * @USER: fhx * @DATE: 2023/1/6 **/ @Aspect @Component public class MeiJiInterceptor extends BaseController { @Resource private MjRequestLogService mjRequestLogService; //机构公钥 private static String jgPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7zFeslbt8BQ3FodPJXKILNQlZiLxmy0VJ6pWbcN/0UxNqoUMTw4IsGdCvJwlyRntL9WeGvrol9XirPH0dMAoua1xM9CkaTXI4dZnpoGVDsJxwQUi0Ld76pnpY8JfcKUZYCc4gGjqonzdIWK5DbK8VRzS2VsxgXG6yHv/HxyYNTQIDAQAB"; //加密算法 public static final String KEY_ALGORITHM = "RSA"; //签名算法 public static final String SIGNATURE_ALGORITHM = "SHA256WithRSA"; private static Logger logger = LoggerFactory.getLogger(MeiJiInterceptor.class.getName()); /** * 设置拦截的位置 * 拦截要校验的包 */ @Pointcut("within(com.hx.phip.meiji..*))") public void checkSign() { System.out.println("拦截啦"); } /* *设置拦截的位置的操作 * @param joinPoint * @return * @throws Throwable*/ @Around("checkSign()") public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable { Object result = "SUCCESS"; //获取签名 HttpServletRequest request = getRequest(); String method = request.getServletPath(); String bodyJson = ""; Integer status = MjRequestLog.STATUS_SUC; String sign = request.getHeader("sign"); String errMsg = null; try { //判断请求类型 String contentType = request.getContentType(); if (StringUtils.noNull(contentType)) { contentType = contentType.toLowerCase(); } if (!MediaType.APPLICATION_JSON_VALUE.equals(contentType)) { throwParamException("请求数据格式非JSON格式!"); } if (StringUtils.isEmpty(sign)) { throwParamException("请求签名为空!"); } String str; // 获取参数, 只取自定义的参数, 自带的HttpServletRequest, HttpServletResponse不管 BufferedReader br = request.getReader(); while ((str = br.readLine()) != null) { bodyJson += str; } JSONObject body = JSONObject.parseObject(unicodeToCN(bodyJson)); //如果走的是皮肤报告接收接口,则参与验证签名的参数只是部分参数 if(method.equals("/meiji/api/report/receive")){ //清除不参与验签的参数 body.remove("created_time"); body.remove("icon"); body.remove("share_url"); body.remove("resources"); body.remove("contents"); body.remove("view_url"); } //验签 verify(splicingParams(body), jgPublicKey, sign); result = joinPoint.proceed(); } catch (Exception e) { logger.error("美际拦截器异常!"); logger.error(GlobalExceptionHandler.getExceptionInformation(e)); errMsg = e.getMessage().length() > 255 ? e.getMessage().substring(0, 250) : e.getMessage(); JSONObject data = new JSONObject(); data.put("code", PlatformCode.ERROR_SYSTEM); data.put("message", errMsg); result = data; status = MjRequestLog.STATUS_FAIL; } finally { //新增请求日志 mjRequestLogService.insert(new MjRequestLog(method, sign, unicodeToCN(bodyJson), errMsg, status)); return result; } } public static void main(String args[]) throws Exception { 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}]}"; JSONObject body = JSONObject.parseObject(unicodeToCN(s)); body.remove("created_time"); body.remove("icon"); body.remove("share_url"); body.remove("resources"); body.remove("contents"); body.remove("view_url"); String sign = "Wb1ZO6v+2ej9eRfs38mUs2dD4Yu7gJJncJHnbRSTDHj5WIK2uQWTe7Berz+heEfvFL/0j8Qx5jezcJoTgoM0hecYA09hKGSDwt2QeKt9BVchcNHvFDC+Aj56bqtB+/nZFbuY8+bfUTIL4xhFxVYznoV3ygY1Ktq2UEt+OY/uAgc="; verify(splicingParams(body), jgPublicKey, sign); } //////////////////////////////////////////////////////////////// /** * 拼接参数 * * @param params 参数 */ @SneakyThrows public static String splicingParams(Map params) { List keys = new ArrayList<>(params.keySet()); Collections.sort(keys); String prestr = ""; for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = params.get(key).toString(); //对中文转码字符转为中文 // value = unicodeToCN(value); if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符 prestr = prestr + key + "=" + value; } else { prestr = prestr + key + "=" + value + "&"; } } return prestr; } /** * 验签 * * @param requestData 数据 * @param publicKey 公钥 * @param sign 数字签名 * @return * @throws Exception */ public static void verify(String requestData, String publicKey, String sign) throws Exception { if (StringUtils.isEmpty(publicKey) || StringUtils.isEmpty(sign)) { throw new TipsException("参数异常!"); } //解密公钥 byte[] keyBytes = new BASE64Decoder().decodeBuffer(publicKey); //构造X509EncodedKeySpec对象 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes); //指定加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); //取公钥匙对象 PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(publicKey2); signature.update(requestData.getBytes()); //验证签名是否正常 try { if(signature.verify(new BASE64Decoder().decodeBuffer(sign))){ logger.info("==美际 验证签名成功=="); }else { throw new TipsException("== 美际 验签失败!=="); } } catch (Exception e) { throw new TipsException(e.getMessage()); } } /** * Unicode转 汉字字符串 * * @param str * @return */ public static String unicodeToCN(String str) { if(StringUtils.isEmpty(str)){ str = ""; } Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))"); Matcher matcher = pattern.matcher(str); char ch; while (matcher.find()) { ch = (char) Integer.parseInt(matcher.group(2), 16); str = str.replace(matcher.group(1), ch + ""); } return str; } /** * 中文转Unicode * 其他英文字母或特殊符号也可进行Unicode编码 * @param cn * @return */ public static String cnToUnicode(String cn) { char[] chars = cn.toCharArray(); StringBuilder returnStr = new StringBuilder(); for (int i = 0; i < chars.length; i++) { returnStr.append("\\u").append(Integer.toString(chars[i], 16)); } return returnStr.toString(); } }