[Java] http connection using agent (example of integrating Alibaba cloud SMS service in Intranet environment)

Posted by Simmo on Wed, 17 Jun 2020 07:11:50 +0200

1. Problem description

I do project services: start in the Intranet environment, all links related to the Internet need to go through transparent agent.
Alibaba cloud sdk: encapsulate all operations related to http. We can't configure HTTP related settings.
To solve the problem 1: how can intranet service access the Internet agent?
How to set up http connection proxy in java code?
Question 2: This is the topic of this blog post, with jdk8 as an example.

2. Implementation method

(1) Use System.properties

//Using HTTP
System.setProperty("http.proxyHost", "xxx.xxx.xxx");//Fill in your proxy ip
System.setProperty("http.proxyPort", "8080");//Fill in your proxy port
(oracle Relevant documents do not proxySet Properties, no need to add)
(If there is relevant password verification, it will not be explained here.)
//Use HTTPS
System.setProperty("https.proxyHost", "webcache.example.com");
System.setProperty("https.proxyPort", "443");

//Supplement, use http.nonProxyHosts  The address in this property will be directly accessed without passing through the proxy
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1|*.local|")

advantage:

  • Simple and fast, two statements can let http connection go through the proxy.
  • If you use HTTP, you don't need to change Alibaba sdk. You don't see that Alibaba cloud sdk can switch http/https settings.

Disadvantages:

  • Once the global System proxy field is set, all connections will be accessed using the proxy. Of course, you can configure it http.nonProxyHosts , but there will be problems when there are agents in the System that cannot access ip.

Can I close the agent after using it?

System.setProperty("http.proxyHost", "xxx.xxx.xxx");//Fill in your proxy ip
System.setProperty("http.proxyPort", "8080");//Fill in your proxy port
//http request (using proxy)
System.clearProperty("http.proxyHost");
//http request (no proxy)

If your program is single threaded, that's fine.
If the program is multithreaded, there is no way to guarantee whether there will be other threads running the agent before clearProperty, then there is a problem.
But if the agent has access to the ip requested by all threads, it doesn't matter.

(2) The ultimate solution: use agents for connections
Here we use Apache's HttpClient as a demonstration:

        HttpHost proxy = new HttpHost(PROXY_IP, PROXY_PORT, "http");
        System.out.println("[Agent information] ip:" + PROXY_IP + "port:" + PROXY_PORT);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        RequestConfig requestConfig = RequestConfig.custom()
	        .setConnectTimeout(5000)  //Set the timeout time for connection establishment
	        .setConnectionRequestTimeout(5000)
	        .setSocketTimeout(5000) 
	        .setProxy(proxy) //Set up proxy
        	.setRedirectsEnabled(true).build();
        httpGet.setConfig(requestConfig);
        return EntityUtils.toString(httpClient.execute(httpGet).getEntity());

3. Example of integrating Alibaba SMS service

It's amazing to see an old brother modifying Ali's jar package directly. In fact, Alibaba http requests to sign the article.
Attach connection: SMS service request signature
I retrofit the request in the above link:

    public static void sendSms(String telephone, String verifierCode, String templateCode) {
        try {
            String sendUrl = getSendRequestUrl(telephone, verifierCode, templateCode);
            String xml = doAction(sendUrl); //The key is doAction
            //Do business by yourself
        } catch (Exception e) {
        }
    }

    private static String doAction(String url) throws IOException {
        HttpHost proxy = new HttpHost(PROXY_IP, PROXY_PORT, "http");
        //Set the agent to request configuration
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // parameter
        // Create Get request
        HttpGet request = new HttpGet(url);
        // configuration information
        RequestConfig requestConfig = RequestConfig.custom()
                // Set connection timeout in milliseconds
                .setConnectTimeout(5000)
                // Set request timeout in milliseconds
                .setConnectionRequestTimeout(5000)
                // socket read / write timeout (in milliseconds)
                .setSocketTimeout(5000)
                .setProxy(proxy)
                // Set whether redirection is allowed (default is true)
                .setRedirectsEnabled(true).build();

        request.setConfig(requestConfig);
        // Execute get request and get response body through entityutilities
        return EntityUtils.toString(httpClient.execute(request).getEntity());
    }

    private static String getSendRequestUrl(String telephone, String verifierCode, String templateCode)
            throws Exception {
        java.text.SimpleDateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));// Make sure to set GMT time zone here
        java.util.Map<String, String> paras = new java.util.HashMap<String, String>();
        // 1. System parameters
        paras.put("SignatureMethod", "HMAC-SHA1");
        paras.put("SignatureNonce", java.util.UUID.randomUUID().toString());
        paras.put("AccessKeyId", accessKeyId);
        paras.put("SignatureVersion", "1.0");
        paras.put("Timestamp", df.format(new java.util.Date()));
        paras.put("Format", "XML");
        // 2. Business API parameters
        paras.put("Action", "SendSms");
        paras.put("Version", "2017-05-25");
        paras.put("RegionId", "cn-shanghai");
        paras.put("PhoneNumbers", telephone);
        paras.put("SignName", "xxxxx");
        paras.put("TemplateParam", "{\"code\":\"" + verifierCode + "\"}");
        paras.put("TemplateCode", templateCode);
        // 3. Remove the signature Key
        if (paras.containsKey("Signature"))
            paras.remove("Signature");
        // 4. KEY sorting
        java.util.TreeMap<String, String> sortParas = new java.util.TreeMap<String, String>();
        sortParas.putAll(paras);
        // 5. Construct the string to be signed
        java.util.Iterator<String> it = sortParas.keySet().iterator();
        StringBuilder sortQueryStringTmp = new StringBuilder();
        while (it.hasNext()) {
            String key = it.next();
            sortQueryStringTmp.append("&").append(specialUrlEncode(key))
                    .append("=").append(specialUrlEncode(paras.get(key)));
        }
        String sortedQueryString = sortQueryStringTmp.substring(1);// Remove the first redundant & symbol
        StringBuilder stringToSign = new StringBuilder();
        stringToSign.append("GET").append("&");
        stringToSign.append(specialUrlEncode("/")).append("&");
        stringToSign.append(specialUrlEncode(sortedQueryString));
        String sign = sign(accessSecret + "&", stringToSign.toString());
        // 6. Special URL coding is also required at the end of signing
        String signature = specialUrlEncode(sign);
        // Finally print out the URL of the legal GET request
        return "http://dysmsapi.aliyuncs.com/?Signature=" + signature + sortQueryStringTmp;
    }

    private static String specialUrlEncode(String value) throws Exception {
        return java.net.URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
    }

    private static String sign(String accessSecret, String stringToSign) throws Exception {
        javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
        mac.init(new javax.crypto.spec.SecretKeySpec(accessSecret.getBytes("UTF-8"), "HmacSHA1"));
        byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
        return new sun.misc.BASE64Encoder().encode(signData);
    }
	

Summary:
This blog post is mainly about http connection using agent and the practice of agent in specific integrated services.
This blog refers to the oracle jdk8 Java Networking and Proxies

Topics: Java Mac SDK Oracle