商户网关调用逻辑

之前都是做后台逻辑,没怎么接触网关接口的代码。最近加入了跨境支付团队,团队大部分的工作就是对接商户,对接银行渠道,对接海关。所以写了很多的网关接口。写网关接口很单调,主要逻辑就是

- 1.加签-发送请求
    - 2.1 处理同步响应:验签-处理内容-加签响应-发送响应
    - 2.2 处理异步响应:异步又分两种。
        - 2.2.1 在第一步时提供了回调接口。验签-处理内容-加签响应-发送响应
        - 2.2.2 通过第一步返回的一个号码去做定时任务查询。加签-发送查询-验签-处理响应内容。

可以看见,在每一次发送请求时,都会加签,在每次接到响应时,都会验签。

商户秘钥

商户在调用跨境系统的网关接口时,需要使用秘钥加签。加签的作用有两个1.防篡改 2.身份识别。我们通过RSA或者MD5两张方式来实现。
RSA是非对称加密,商户持有私钥,用来加签。跨境系统持有公钥,用来验签。如商户发送内容:明文+摘要(明文用私钥加签),系统接收验证:摘要+公钥=明文

MD5是对称的,只能做防篡改,我们在计算摘要时将MD5值接到内容末尾,就可以实现身份识别。商户发送内容:明文+摘要(明文+MD5再用MD5散列),系统接收验证(明文+MD5串再用MD5散列,看是不是和用户传过来摘要一样)。

注意,明文在加签时,需要排序,以便在商户和跨境系统得到统一的字符串。

业务实现

在商户后台,允许商户下载秘钥(MD5,或是RSA),每次下载都会使得之前的秘钥失效。如果是MD5,让用户下载一个包含MD5的文件,同时跨境系统也会将这个值更新到数据库。如果是RSA,让用户下载一个包含公私钥的文件,同时跨境系统将其中的公钥更新到数据库。

以上是基本的实现方案。以下是一些相关的代码

关键代码

//读取request中的值,现在我有两个方法,还不清楚两者的区别。一个是通过读流的方式,一个是通过getParameterMap的方式。
private Map<String, String> getParams(HttpServletRequest request){
        Map<String, String> params = new HashMap<String, String>();
        StringBuffer sb = new StringBuffer();
        BufferedReader read = null;
        try{
            read = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));
            String line = null;
            while((line = read.readLine()) != null){
                sb.append(line);
            }
        }
        catch (Exception e){
            logger.info("读取流异常");
        }
        finally {
            try {
                if(read != null)
                    read.close();
            } catch (IOException e) {
                logger.info("IO关闭异常");
            }
        }
        logger.info("notify返回值:" + sb.toString());
        params = JSON.parseObject(sb.toString(), Map.class);
        return params;
    }
private Map<String,String> getParams(HttpServletRequest request){
        Map<String, String> map  = new HashMap<String,String>();
        Enumeration enu=request.getParameterNames();  
        while(enu.hasMoreElements()){
            String paraName=(String)enu.nextElement();
            map.put(paraName,request.getParameter(paraName));
        }
        
        return map;
    }
//request中参数排序,返回有序的明文字符串
public static String createLinkString(Map<String, String> map){
        List<String> list = new ArrayList<String>(map.keySet());
        Collections.sort(list);
        StringBuffer sb = new StringBuffer();
        
        for(int i=0; i<list.size(); i++){
            String key = list.get(i);
            String value = map.get(key);
            if(i == list.size() - 1){
                sb.append(key).append("=").append(value);
            }
            else {
                sb.append(key).append("=").append(value).append("&");
            }
        }
        return sb.toString();
}
//跨境网关验签
public boolean verifyBySignType(String src, String signMsg,
            String signType, String key, String charsetType) throws Exception {
        int charset = Integer.valueOf(charsetType).intValue();
        CharsetTypeEnum charsetin = CharsetTypeEnum.getByCode(charset);
        if (SignTypeEnum.getByCode(signType) == null) {
            log.error("@FI-加签类型不正确");
            throw new ParamValidateException("FI-加签类型不正确",
                    ExceptionCodeEnum.ILLEGAL_PARAMETER);
        }
        if (charsetin == null) {
            log.error("@FI-字符集类型不正确");
            throw new ParamValidateException("FI-字符集类型不正确",
                    ExceptionCodeEnum.ILLEGAL_PARAMETER);
        }

        if (null != signType && signType.equals(SignTypeEnum.RSA.getCode())) {// RSA
            try {
                return SecuritySubstance.verifySignatureByRSA(src, signMsg,
                        charsetin, key);
            } catch (Exception e) {
                log.error("RSA验签:验签过程异常" + e);
                throw new Exception(e);
            }
        } else if (null != signType
                && signType.equals(SignTypeEnum.MD5.getCode())) {
            try {
                return SecuritySubstance.verifySignatureByMD5(src, signMsg,
                        charsetin, key);
            } catch (Exception e) {
                log.error("MD5验签:验签过程异常" , e);
                throw new Exception(e);
            }
        }
        return false;
    }
//MD5验签
/**
     * 校验签名MD5
     * @param src 原数据
     * @param dit 加签后数据
     * @return result
     */
    public static boolean verifySignatureByMD5(String src, String dit,
            CharsetTypeEnum charsetType,String parnterPublicKey) throws Exception {
        
        src += pkeyHeader+parnterPublicKey;
        String mac = null;
        try{
            mac = MD5BaseAlgorithms.getMD5Str(src);
        }catch(Exception e){
            System.err.println("MD5 验签出现异常");
            e.printStackTrace();
            return false;
        }
        if(dit.equals(mac)){
            return true;
        }
        return false;
    }
//RSA验签
/**
     * 校验签名RSA
     * @param src 原数据
     * @param dit 加签后数据
     * @param publicKey Base64后的公钥
     * @return result
     */
    public static boolean verifySignatureByRSA(String src, String dit,
            CharsetTypeEnum charsetType, String publicKey) throws Exception {
        boolean result = false;
        int hashCode = HashAlgorithms.PJWHash(src);
        String hashSrc = hashCode+"";
        RSAAlgorithms sign = new RSAAlgorithms();
        
        try {
            result = 
                sign.verifySignature(hashSrc.getBytes(), dit, ByteArrayUtil.toByteArray(publicKey));
        
        } catch (Exception e) {
            System.err.println("RSA 验签出现异常");
            e.printStackTrace();
            result = false;
        }    
        return result;
    }

沈子平
183 声望17 粉丝

慢慢积累,一切都不会太晚.