支付网关的设计

可以参见对网关的说明:

   网关:https://blog.csdn.net/ganjing222/article/details/53924402

   支付框架:https://blog.csdn.net/huangshulang1234/article/details/78726195

   RAS工具:http://web.chacuo.net/netrsakeypair

   JAVA反射:https://www.cnblogs.com/zhengtu2015/p/5829740.html

业务背景:

  在一个支付平台中,一个商户可以有效,安全,快捷的接入是至关重要的。支付网关的存在最重要的功能和职责,是验证请求方的数据安全,有效,同时,要转发这笔请求到后端的支持系统。

网关设计:

  

RSAUtil2工具类,提供RSA的加密方式:
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

public class RSAUtil2 {
    public static final String ENCRYPTION_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    /**
     * 生成密钥
     */
    public static Map<String, Object> initKey() throws Exception {
        /* 初始化密钥生成器 */
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ENCRYPTION_ALGORITHM);
        keyPairGenerator.initialize(1024);

        /* 生成密钥 */
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put("PublicKey", publicKey);
        keyMap.put("PrivateKey", privateKey);
        return keyMap;
    }

    /**
     * 取得公钥
     */
    public static String getPublicKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get("PublicKey");
        return Base64Util2.encryptBASE64(key.getEncoded());
    }

    /**
     * 取得私钥
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get("PrivateKey");
        return Base64Util2.encryptBASE64(key.getEncoded());
    }

    /**
     * 加密
     */
    public static byte[] encrypt(byte[] data, String keyString, boolean isPublic) throws Exception {
        Map<String, Object> keyAndFactoryMap = RSAUtil2.generateKeyAndFactory(keyString, isPublic);
        KeyFactory keyFactory = RSAUtil2.getKeyFactory(keyAndFactoryMap);
        Key key = RSAUtil2.getKey(keyAndFactoryMap);

        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, key);

        return cipher.doFinal(data);
    }

    /**
     * 解密
     */
    public static byte[] decrypt(byte[] data, String keyString, boolean isPublic) throws Exception {
        Map<String, Object> keyAndFactoryMap = RSAUtil2.generateKeyAndFactory(keyString, isPublic);
        KeyFactory keyFactory = RSAUtil2.getKeyFactory(keyAndFactoryMap);
        Key key = RSAUtil2.getKey(keyAndFactoryMap);

        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, key);

        return cipher.doFinal(data);
    }

    /**
     * 生成钥匙
     */
    public static Map<String, Object> generateKeyAndFactory(String keyString, boolean isPublic) throws Exception {
        byte[] keyBytes = Base64Util2.decryptBASE64(keyString);

        KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTION_ALGORITHM);
        Key key = null;
        if (isPublic) {
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
            key = keyFactory.generatePublic(x509KeySpec);
        } else {
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            key = keyFactory.generatePrivate(pkcs8KeySpec);
        }

        Map<String, Object> keyAndFactoryMap = new HashMap<String, Object>(2);
        keyAndFactoryMap.put("key", key);
        keyAndFactoryMap.put("keyFactory", keyFactory);

        return keyAndFactoryMap;
    }

    /**
     * 从指定对象中获取钥匙
     */
    public static Key getKey(Map<String, Object> map) {
        if (map.get("key") == null) {
            return null;
        }
        return (Key)map.get("key");
    }

    /**
     * 从指定对象中获取钥匙工厂
     */
    public static KeyFactory getKeyFactory(Map<String, Object> map) {
        if (map.get("keyFactory") == null) {
            return null;
        }
        return (KeyFactory)map.get("keyFactory");
    }

    /**
     * 对信息生成数字签名(用私钥)
     */
    public static String sign(byte[] data, String keyString) throws Exception {
        Map<String, Object> keyAndFactoryMap = RSAUtil2.generateKeyAndFactory(keyString, false);
        Key key = RSAUtil2.getKey(keyAndFactoryMap);

        PrivateKey privateKey = (PrivateKey)key;

        // 用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data);

        return Base64Util2.encryptBASE64(signature.sign());
    }

    /**
     * 校验数字签名(用公钥)
     */
    public static boolean verify(byte[] data, String keyString, String sign)
            throws Exception {
        Map<String, Object> keyAndFactoryMap = RSAUtil2.generateKeyAndFactory(keyString, true);
        Key key = RSAUtil2.getKey(keyAndFactoryMap);

        PublicKey publicKey = (PublicKey)key;

        // 取公钥匙对象
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data);

        // 验证签名是否正常
        return signature.verify(Base64Util2.decryptBASE64(sign));
    }

}

 Base64Util2工具类,提供防中文乱码的(加解密)方式:

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Base64Util2 {

    /**
     * Base64加密
     */
    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    /**
     * Base64解密
     */
    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }


}


DESUtil2 工具包,提供DES 的对称加解密算法:

import com.qudian.pay.gateway.constance.C;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.SecureRandom;

 
public class DESUtil2 {

    /**
     * 加密
     *
     * @param content 待加密内容
     * @param key     加密的密钥
     * @return
     */
    public static byte[] encrypt(String content, String key) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(key.getBytes());
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
            byte[] result = cipher.doFinal(content.getBytes());
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密
     *
     * @param content     待加密内容
     * @param key         密钥
     * @param charsetName 字符集
     * @return
     */
    public static String encrypt2Str(String content, String key, String charsetName) {
        try {
            byte[] bytes = encrypt(content, key);
            if (StringUtils.isBlank(charsetName)) charsetName = C.DEFAULT_CHAREST;
            return new String(bytes, charsetName);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密
     *
     * @param content 待解密内容
     * @param key     解密的密钥
     * @return
     */
    public static String decrypt(byte[] content, String key) throws Exception {
        SecureRandom random = new SecureRandom();
        DESKeySpec desKey = new DESKeySpec(key.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(desKey);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.DECRYPT_MODE, securekey, random);
        byte[] result = cipher.doFinal(content);
        return new String(result);
    }

    /**
     * 解密
     *
     * @param content 待解密内容
     * @param key     解密的密钥
     * @return
     */
    public static String decrypt(String content, String key) throws Exception{
        return decrypt(content.getBytes(C.DEFAULT_CHAREST), key);
    }

    /**
     * 解密
     *
     * @param content     待解密内容
     * @param key         密钥
     * @param charsetName 字符集,默认为UTF-8
     * @return
     */
    public static String decryptByStr(String content, String key, String charsetName) {
        if (StringUtils.isBlank(charsetName)) charsetName = C.DEFAULT_CHAREST;
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(key.getBytes());
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.DECRYPT_MODE, securekey, random);
            byte[] result = cipher.doFinal(content.getBytes(charsetName));
            return new String(result);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
}

怎样使用:

import static org.junit.Assert.*;

import com.alibaba.dubbo.common.io.Bytes;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.sun.javafx.scene.control.skin.VirtualFlow;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class How2Use {
    private String publicKey = null;
    private String privateKey = null;

    @Before
    public void setUp() throws Exception {
        Map<String, Object> keyMap = RSAUtil2.initKey();
        publicKey = RSAUtil2.getPublicKey(keyMap);
        privateKey = RSAUtil2.getPrivateKey(keyMap);

        publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqpGuBelqd9gceb2a6y8W0xhRz\n" +
                "f6O9h0ALrCwIxsHUFF2YU0nUpAfrR/LbbpSmCEClOLwh80ZBf6ea8GDKubHaQvma\n" +
                "vdMgFzs0lWltWUzT1dbXGd3ahobOpTk7dceWIXcbB0SgQ/zENqCAl9lyJUFnXsbJ\n" +
                "eEh6HSf1+/stIAJpAQIDAQAB";
        privateKey="MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKqka4F6Wp32Bx5v\n" +
                "ZrrLxbTGFHN/o72HQAusLAjGwdQUXZhTSdSkB+tH8ttulKYIQKU4vCHzRkF/p5rw\n" +
                "YMq5sdpC+Zq90yAXOzSVaW1ZTNPV1tcZ3dqGhs6lOTt1x5YhdxsHRKBD/MQ2oICX\n" +
                "2XIlQWdexsl4SHodJ/X7+y0gAmkBAgMBAAECgYB7wvxl0APhBQtsu4dae2hNEEzM\n" +
                "6Aot7+Y3kQvu4Zi65HptfBrUKpPDxU8Sb0VIljusmVV5miOeZaN98FGDNuMSJnDX\n" +
                "CmYUGjJTijxS1Ao6ft3QsSkyQK5ZkUOU8JxPzyr3QQbvWwHz426WxSNZWX9l5SDv\n" +
                "SfTiTyBW+MGeLJ7tAQJBANtZMedQhHLt9+msxr7sBFwrfenmSvEke2y8yG/99tn5\n" +
                "b4+vcAZbe/fiy8SduJQ2fUhNyPcDc7GZlAPWyVEGOYkCQQDHJ8e4FbxdXikmRsJ6\n" +
                "bHOmx40RwMd6fqR7/zSszoKhHjKx6ROI1LZTBYUmrlJ/nCnEZhAmw9P3KiFKSKy4\n" +
                "h+25AkAgVpAS6D3+q7LHEp/iee1SoV7Y4wjdcGF58IMkb667CBk4d8buSQKH0znz\n" +
                "bMVPpJDn3f/o2uttnZTC1CGt/lrxAkBB5ykRAqhVoU4uq2W5y1+uBgTppgicwYG+\n" +
                "wqH0uVXC6VJPS1i6g+FdscrxFOiZ79xmcHvRyCXBecCQa2I0ok6JAkAoV6H2ApSB\n" +
                "2BJHM27nFG8kjb7cajoPcLxPND1BPR7y1dQKBYEG1qOcb6sG+cknd1k+UOgDoeQq\n" +
                "K3eH1zw2Txps";
        System.out.println("公钥 -> " + publicKey);
        System.out.println("私钥 -> " + privateKey);
    }

    @Test
    public void test() throws Exception {
        System.out.println("公钥加密,私钥解密");
        String sourceString = "hi, RSA";

        byte[] encodedData = RSAUtil2.encrypt(sourceString.getBytes(), publicKey, true);
        byte[] decodedData = RSAUtil2.decrypt(encodedData, privateKey, false);

        String targetString = new String(decodedData);
        System.out.println("加密前: " + sourceString + ",解密后: " + targetString);
        assertEquals(sourceString, targetString);
    }

    @Test
    public void test2() throws Exception {
        System.out.println("私钥签名,公钥验证签名");
        String sourceString = "hello, RSA sign";
        sourceString="{\"amount\":200000,\"merchantId\":\"MR0000001\",\"merchantIp\":\"192.168.1.1\",\"merchantNo\":\"201803060000000000000004\",\"transCur\":\"CNY\",\"transTime\":1526537403392}";
        byte[] data = sourceString.getBytes();

        // 产生签名
        String sign = RSAUtil2.sign(data, privateKey);
        System.out.println("签名 -> " + sign);

        // 验证签名
        boolean status = RSAUtil2.verify(data, publicKey, sign);
        System.out.println("状态 -> " + status);
        assertTrue(status);
    }

    @Test
    public void test3() throws Exception {
        System.out.println("私钥加密,公钥解密");
        String sourceString = "hello, reRSA";
        byte[] data = sourceString.getBytes();

        byte[] encodedData = RSAUtil2.encrypt(data, privateKey, false);
        byte[] decodedData = RSAUtil2.decrypt(encodedData, publicKey, true);

        String targetString = new String(decodedData);
        System.out.println("加密前: " + sourceString + ",解密后: " + targetString);
        assertEquals(sourceString, targetString);
    }
 }


JSON 数据和对象互转过程:

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;

/**
 * Created by xun on 2015/9/1.
 * <p>
 * JSON Utils
 */
public class JsonParser {

    static Logger logger = LoggerFactory.getLogger(JsonParser.class);

    private static ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    /**
     * 将对象序列化为JSON字符串
     *
     * @param object
     * @return JSON字符串
     */
    public static String serialize(Object object) {
        Writer write = new StringWriter();
        try {
            objectMapper.writeValue(write, object);
        } catch (JsonGenerationException e) {
            logger.error("JsonGenerationException when serialize object to json", e);
        } catch (JsonMappingException e) {
            logger.error("JsonMappingException when serialize object to json", e);
        } catch (IOException e) {
            logger.error("IOException when serialize object to json", e);
        }
        return write.toString();
    }

    /**
     * 将JSON字符串反序列化为对象
     *
     * @param json
     * @return JSON字符串
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserialize(String json, Class<T> clazz) throws IOException {
        Object object = null;
        object = objectMapper.readValue(json, TypeFactory.rawClass(clazz));
        return (T) object;
    }

    /**
     * 将JSON字符串反序列化为对象
     *
     * @param json
     * @return JSON字符串
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserialize(InputStream json, Class<T> clazz) {
        Object object = null;
        try {
            object = objectMapper.readValue(json, TypeFactory.rawClass(clazz));
        } catch (JsonParseException e) {
            logger.error("JsonParseException when serialize object to json", e);
        } catch (JsonMappingException e) {
            logger.error("JsonMappingException when serialize object to json", e);
        } catch (IOException e) {
            logger.error("IOException when serialize object to json", e);
        }
        return (T) object;
    }

    /**
     * 将JSON字符串反序列化为对象
     *
     * @param json
     * @return JSON字符串
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserialize(String json, TypeReference<T> typeRef) {
        try {
            return (T) objectMapper.readValue(json, typeRef);
        } catch (JsonParseException e) {
            logger.error("JsonParseException when deserialize json", e);
        } catch (JsonMappingException e) {
            logger.error("JsonMappingException when deserialize json", e);
        } catch (IOException e) {
            logger.error("IOException when deserialize json", e);
        }
        return null;
    }

    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return objectMapper.getTypeFactory().constructParametrizedType(collectionClass, collectionClass, elementClasses);
    }

    /**
     * 将JSON字符串反序列化为对象
     *
     * @param json
     * @return JSON字符串
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserialize(String json, JavaType javaType) {
        Object object = null;
        try {
            object = objectMapper.readValue(json, javaType);
        } catch (JsonParseException e) {
            logger.error("JsonParseException when serialize object to json", e);
        } catch (JsonMappingException e) {
            logger.error("JsonMappingException when serialize object to json", e);
        } catch (IOException e) {
            logger.error("IOException when serialize object to json", e);
        }
        return (T) object;
    }

    public static JsonNode deserialize(String json) {
        try {
            return objectMapper.readTree(json);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}



我们需要把调用业务系统的方法,进行配置,无需在代码中写死。

在系统中支持DUBBO 的 HESSIAN 两种协议。

import com.qudian.pay.gateway.entity.dto.MethodInfo;
import com.qudian.pay.gateway.entity.dto.MethodMap;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
 
@Component
public class InitService {

    private static final Logger logger = LoggerFactory.getLogger(InitService.class);

    @Resource(name = "dubboServiceCodeMap")
    private Map<String, Object> dubboServiceCodeMap;

    @Resource(name = "hessianServiceCodeMap")
    private Map<String, Object> hessianServiceCodeMap;

    static Set<String> hessianIlLegal = new HashSet<>();

    /**
     * hessian自带的方法,需要排除
     */
    static {
        hessianIlLegal.add("equals");
        hessianIlLegal.add("toString");
        hessianIlLegal.add("hashCode");
        hessianIlLegal.add("indexOf");
        hessianIlLegal.add("isFrozen");
        hessianIlLegal.add("setPreFiltered");
        hessianIlLegal.add("setExposeProxy");
        hessianIlLegal.add("setTargetSource");
        hessianIlLegal.add("getTargetClass");
        hessianIlLegal.add("getTargetSource");
        hessianIlLegal.add("getProxiedInterfaces");
        hessianIlLegal.add("isProxyTargetClass");
        hessianIlLegal.add("isExposeProxy");
        hessianIlLegal.add("isPreFiltered");
        hessianIlLegal.add("isInterfaceProxied");
        hessianIlLegal.add("getAdvisors");
        hessianIlLegal.add("addAdvice");
        hessianIlLegal.add("addAdvisor");
        hessianIlLegal.add("removeAdvisor");
        hessianIlLegal.add("replaceAdvisor");
        hessianIlLegal.add("removeAdvice");
        hessianIlLegal.add("toProxyConfigString");
    }

    @PostConstruct
    public void init() throws BeansException {
        logger.info("开始加载自定义map");
        if (CollectionUtils.isEmpty(dubboServiceCodeMap) && CollectionUtils.isEmpty(hessianServiceCodeMap)) {
            logger.info("自动注入map为空,加载结束");
            return;
        }
        Map<String, MethodInfo> methodMap = new HashMap<String, MethodInfo>();
        //处理dubbo注入
        for (String serviceName : dubboServiceCodeMap.keySet()) {
            Object serviceInteface = dubboServiceCodeMap.get(serviceName);
            Method[] methods = serviceInteface.getClass().getDeclaredMethods();
            for (Method method : methods) {
                String methodName = method.getName();
                if (methodName.equals("$echo")) continue;
                Class[] methodParams = method.getParameterTypes();
                Class resp = method.getReturnType();
                MethodInfo methodInfo = new MethodInfo();
                methodInfo.setInteface(serviceInteface);
                methodInfo.setMethodName(method);
                methodInfo.setParam(methodParams[0]);
                methodInfo.setResp(resp);
                methodMap.put(serviceName + "." + methodName, methodInfo);
                logger.info("Dubbo已加载:{}.{}", serviceName, methodName);
            }
        }
        //处理hessian注入
        for (String serviceName : hessianServiceCodeMap.keySet()) {
            Object serviceInteface = hessianServiceCodeMap.get(serviceName);
            Method[] methods = serviceInteface.getClass().getDeclaredMethods();
            for (Method method : methods) {
                String methodName = method.getName();
                if (isIlLegal(methodName)) continue;
                Class[] methodParams = method.getParameterTypes();
                Class resp = method.getReturnType();
                MethodInfo methodInfo = new MethodInfo();
                methodInfo.setInteface(serviceInteface);
                methodInfo.setMethodName(method);
                methodInfo.setParam(methodParams[0]);
                methodInfo.setResp(resp);
                methodMap.put(serviceName + "." + methodName, methodInfo);
                logger.info("hessian已加载:{}.{}", serviceName, methodName);
            }
        }
        MethodMap.setMethodMap(methodMap);
        logger.info("自定义map加载完毕");
    }

    /**
     * 判断方法是否是hessian自带的,如果是返回true
     *
     * @param hessionMethodName
     * @return
     */
    private Boolean isIlLegal(String hessionMethodName) {
        if (StringUtils.isBlank(hessionMethodName)) return true;
        return hessianIlLegal.contains(hessionMethodName);
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:reference interface="com.xxx.pay.trade.facade.PaymentOrderFacade" id="pay" registry="gateway"
                     group="${dubbo.service.group}" version="${dubbo.service.version}" retries="0" timeout="20000"
                     check="false"/>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<bean id="dubboServiceCodeMap" class="org.springframework.beans.factory.config.MapFactoryBean">
		<property name="targetMapClass" value="java.util.HashMap"/>
		<property name="sourceMap">
			<map>
				<entry key="com.xxx.pay.trade.facade.PaymentOrderFacade" value-ref="pay"/>
				
			</map>
		</property>
	</bean>

	<!--hessian接口Map-->
	<bean id="hessianServiceCodeMap" class="org.springframework.beans.factory.config.MapFactoryBean">
		<property name="targetMapClass" value="java.util.HashMap"/>
		<property name="sourceMap">
			<map>
				<entry key="com.xxx.pay.bills.common.api.IBillsFacade" value-ref="billsFacade"/>
			</map>
		</property>
	</bean>

</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
	   xmlns:beans="http://www.springframework.org/schema/beans"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">


	<bean id="timeoutHessionProxyFactory" class="com.caucho.hessian.client.HessianProxyFactory">
		<property name="connectTimeout" value="${bills.connect.timeout}"/>
		<property name="readTimeout" value="${bills.read.timeout}"/>
	</bean>

	<bean id="billsFacade" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
		<property name="serviceUrl" value="${bills.server.url}/hessian/billFacade"/>
		<property name="serviceInterface" value="com.xxx.pay.bills.common.api.IBillsFacade"/>
		<property name="proxyFactory" ref="timeoutHessionProxyFactory"/>
	</bean>

</beans>

调用使用:doHandler 这块可以参考,其他的实现,无所谓了。

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.qudian.pay.gateway.dto.DoHandlerVo;
import com.qudian.pay.gateway.dto.HttpRequestDto;
import com.qudian.pay.gateway.dto.HttpResponseDto;
import com.qudian.pay.gateway.entity.BizInfo;
import com.qudian.pay.gateway.entity.BizKey;
import com.qudian.pay.gateway.entity.dto.MethodInfo;
import com.qudian.pay.gateway.entity.dto.MethodMap;
import com.qudian.pay.gateway.enums.ExceptionEnums;
import com.qudian.pay.gateway.exception.GatewayException;
import com.qudian.pay.gateway.service.BizDetailService;
import com.qudian.pay.gateway.service.BizInfoService;
import com.qudian.pay.gateway.service.BizKeyService;
import com.qudian.pay.gateway.service.GatewayDetailService;
import com.qudian.pay.gateway.service.GatewayService;
import com.qudian.pay.gateway.service.WhiteIpService;
import com.qudian.pay.gateway.utils.JsonParser;
import com.qudian.pay.gateway.utils.LogUtil;
import com.qudian.pay.gateway.utils.VerfyUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Set;

/**
 * 业务线请求处理
 */
public class HttpServiceImpl implements GatewayService<HttpRequestDto, HttpResponseDto> {

    private static Logger logger = LoggerFactory.getLogger(HttpServiceImpl.class);

    @Resource
    private BizInfoService bizInfoServiceImpl;

    @Resource
    private BizDetailService bizDetailServiceImpl;

    @Resource
    private WhiteIpService whiteIpServiceImpl;

    @Resource
    private BizKeyService bizKeyServiceImpl;

    @Resource
    private GatewayDetailService gatewayDetailServiceImpl;

    private final static Set<String> whiteListServiceCode;

    static {
        whiteListServiceCode = new HashSet<>();
        whiteListServiceCode.add("com.qufenqi.insurance.facade.InsuranceNotifyFacade.tkRefundNotify");
 
    }

    /**
     * 参数验证
     *
     * @param req
     * @return
     */
    @Override
    public void validate(HttpRequestDto req) throws GatewayException {
        if(req==null){
            throw new GatewayException(ExceptionEnums.REQUEST_NULL);
        }
        String bizCode = req.getBizCode();
        String serviceCode = req.getServiceCode();

        if(org.apache.commons.lang3.StringUtils.isBlank(bizCode)){
            throw new GatewayException(ExceptionEnums.REQ_BIZCODE_NULL);
        }
        if(org.apache.commons.lang3.StringUtils.isBlank(req.getContext())){
            throw new GatewayException(ExceptionEnums.REQ_CONTEXT_NULL);
        }
        if(org.apache.commons.lang3.StringUtils.isBlank(serviceCode)){
            throw new GatewayException(ExceptionEnums.REQ_SERVICECODE_NULL);
        }
        if(org.apache.commons.lang3.StringUtils.isBlank(req.getSign())){
            throw new GatewayException(ExceptionEnums.REQ_SIGN_NULL);
        }

        BizInfo bizInfo = bizInfoServiceImpl.queryBizInfoByCache(bizCode);
        if(bizInfo==null){
            throw new GatewayException(ExceptionEnums.QUERY_BIZCODE_NULL);
        }
        if(bizDetailServiceImpl.checkBizDetail(bizCode,serviceCode)){
            throw new GatewayException(ExceptionEnums.QUERY_SERVICE_CODE_NULL);
        }
        if(bizInfo.getIsValidateIp()){
            if(!whiteIpServiceImpl.checkIp(req.getIp(),bizCode)){
                throw new GatewayException(ExceptionEnums.IP_NULL);
            }
        }
    }

    /**
     * 处理前
     *
     * @param req
     */
    @Override
    public void beforeHandler(HttpRequestDto req) throws GatewayException{
        req.setServiceCode(gatewayDetailServiceImpl.getServiceCodeByName(req.getServiceCode()));
        BizKey bizKey = bizKeyServiceImpl.getBizKey(req.getBizCode(),req.getKeyGroupName());
        if(!VerfyUtil.verfySign(bizKey.getSignType(),req.getContext(),req.getSign(),bizKey.getSignKey())){
            String logSignKey = bizKey.getSignKey();
            if (StringUtils.isNotBlank(logSignKey) && logSignKey.length()>=2) {
                logSignKey = logSignKey.substring(logSignKey.length()-2);
            }
            LogUtil.error(logger,"验签失败,SignType:{0},Context:{1},Sign:{2},SignKey:{3},bizKey:{4}",
                    bizKey.getSignType(),req.getContext(),req.getSign(),logSignKey,bizKey);
            throw new GatewayException(ExceptionEnums.UNSIGN_ERROR);
        }
        String context = VerfyUtil.decrypt(bizKey.getEncryptType(),req.getContext(),bizKey.getEncryptKey());
        logger.info("收到业务线请求:{}",context);
        req.setContext(context);
    }

    /**
     * 处理中
     *
     * @param req
     * @return
     */
    @Override
    public DoHandlerVo doHandler(HttpRequestDto req) throws GatewayException{
        if (!whiteListServiceCode.contains(req.getServiceCode())) {
            throw new GatewayException(ExceptionEnums.QUERY_SERVICE_CODE_NULL);
        }
        DoHandlerVo vo = new DoHandlerVo();
        MethodInfo methodInfo = MethodMap.getMethodMap().get(req.getServiceCode());
        if (methodInfo == null) {
            throw new GatewayException(ExceptionEnums.INVOKE_UNIMPL_METHOD);
        }
        Object params = null;
        try {
            params = JSONObject.parseObject(req.getContext(), methodInfo.getParam());
        } catch (JSONException je) {
            logger.error("jSON字符串转换对象异常", je);
            throw new GatewayException(ExceptionEnums.PARAM_PARSE_OBJECT_ERROR);
        } catch (Exception e) {
            logger.error("jSON字符串转换对象未知异常", e);
            throw new GatewayException(ExceptionEnums.PARAM_ERROR);
        }
        try {
            long start = System.currentTimeMillis();
            Object object = methodInfo.getMethodName().invoke(methodInfo.getInteface(), params);
            vo.setObject(object);
            vo.setTimes(System.currentTimeMillis() - start);
            return vo;
        } catch (InvocationTargetException ite) {
            logger.error("调用第三方接口异常", ite);
            throw new GatewayException(ExceptionEnums.INVOKE_METHOD_ERROR);
        } catch (Exception e) {
            logger.error("接口调用失败", e);
            throw new GatewayException(ExceptionEnums.HANDLER_ERROR);
        }
    }

   /**
     * 处理后
     *
     * @param respDto
     */
    @Override
    public HttpResponseDto afterHandler(Object respDto) {
        return HttpResponseDto.build(ExceptionEnums.SUCCESS,JsonParser.serialize(respDto));
    }


}

所需要的SQL表:

CREATE TABLE `biz_key` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `biz_id` int(11) DEFAULT NULL COMMENT '合作伙伴唯一ID',
  `status` tinyint(2) DEFAULT NULL COMMENT '密钥状态',
  `key_group_name` varchar(128) DEFAULT NULL COMMENT '密钥组名称',
  `encrypt_type` varchar(16) DEFAULT NULL COMMENT '加密类型',
  `encrypt_key` varchar(256) DEFAULT NULL COMMENT '加密密钥',
  `sign_type` varchar(16) DEFAULT NULL COMMENT '签名类型',
  `sign_key` varchar(1024) DEFAULT NULL COMMENT '签名密钥',
  `operator` varchar(32) DEFAULT NULL COMMENT '操作人',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '修改时间',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# Dump of table gateway_detail
# ------------------------------------------------------------

DROP TABLE IF EXISTS `gateway_detail`;

CREATE TABLE `gateway_detail` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `service_code` varchar(255) DEFAULT NULL COMMENT '服务编码',
  `service_name` varchar(128) DEFAULT NULL COMMENT '服务名称',
  `owner` varchar(32) DEFAULT '' COMMENT '服务拥有者',
  `version` varchar(16) DEFAULT NULL COMMENT '接口版本',
  `status` tinyint(2) DEFAULT NULL COMMENT '是否可用',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '修改时间',
  `operator` varchar(32) DEFAULT NULL COMMENT '操作人',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  UNIQUE KEY `gateway_detail_scode` (`service_code`) USING BTREE,
  UNIQUE KEY `gateway_detail_sname` (`service_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO gateway.`biz_key` (`id`, `biz_id`, `status`, `key_group_name`, `encrypt_type`, `encrypt_key`, `sign_type`, `sign_key`, `operator`, `create_time`, `update_time`, `remark`)
VALUES
	(1,1,0,'','AES','45bE9Lfcu2Ppkmnm','MD5','Nrrw7YGldC','xiaoming',now(),now(),NULL);


INSERT INTO gateway.`gateway_detail` (`id`, `service_code`, `service_name`, `owner`, `version`, `status`, `create_time`, `update_time`, `operator`, `remark`)
VALUES
	(1,'com.xxx.pay.trade.facade.PaymentOrderFacade.payment','trade.payment','xiaoming','1.0.0',0,now(),now(),NULL,NULL),
	(2,'com.xxx.pay.trade.facade.PaymentOrderFacade.paymentQuery','trade.paymentQuery','xiaoming','1.0.0',0,now(),now(),NULL,NULL);









                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值