基于HmacSM3算法的签名鉴权方式说明

更新时间:2023-12-11 16:35:38

1.公共请求头参数:

在原本公共请求头里新增X-Tsign-Content-Hash-Algorithm和X-Tsign-Open-Ca-Signature-Algorithm,原本的Content-MD5替换为X-Tsign-Content-Hash,其余请求头参数不变。

参数名称

参数

类型

参数必选

参数说明

(左右拖动查询完整描述)

X-Tsign-Open-App-Id

string

应用ID,通过e签宝开放平台获取。

沙箱模拟的应用ID获取方式详见此处

正式生产的应用ID获取方式详见此处

X-Tsign-Open-Auth-Mode

string

接口鉴权方式,请填写固定值 Signature 。

X-Tsign-Open-Ca-Signature

string

请求签名值,点击查看请求签名值计算说明

X-Tsign-Open-Ca-Timestamp

string

接口调用时的Unix时间戳,单位毫秒。

时间戳有效期为15分钟,但建议传入每次接口调用时的当前时间戳。

Accept

string

响应数据类型,建议统一填写 */*

Content-Type

string

请求Body体数据的格式类型;例如:application/json; charset=UTF-8

GET 和 DELETE 请求且Body体无数据时,此参数可为 ""(空字符串)或不传此参数。

X-Tsign-Content-Hash

string

基于SM3算法的请求体body的摘要值(与原有的 Content-MD5 含义完全一致的 header),点击查看Hash计算方法

GET 和 DELETE 请求且Body体无数据时,此参数可为 ""(空字符串)或不传此参数。

X-Tsign-Content-Hash-Algorithm

string

固定值:SM3

X-Tsign-Open-Ca-Signature-Algorithm

string

固定值:HmacSM3

2.请求签名鉴权原理说明

(1)开发者计算请求签名的步骤如下:

  1. 按照e签宝 API 网关的约定方式,拼接生成一个用来进行签名计算的待签名字符串
  2. 使用 AppSecret 对待签名字符串进行 HmacSM3 加密处理,计算得出请求签名值
  3. 将请求签名值及其他头部信息添加到Header请求头中,同时将请求参数与Header请求头一起构造成最终HTTP请求并进行发送。

(2)e签宝 API 网关验证签名的步骤如下:

  1. 从接收到的请求中提取关键数据,拼接生成一个用来进行签名计算的待签名字符串
  2. 从接收到的请求头 X-Tsign-Open-App-Id 参数中读取开发者AppID,并查询其对应的 AppSecret;
  3. 使用 AppSecret 对待签名字符串进行 HmacSM3 加密处理,计算得出请求签名值
  4. 从接收到的请求头 X-Tsign-Open-Ca-Signature 参数中读取开发者计算的签名值,并对比e签宝 API 网关计算的签名值和开发者计算的签名值是否一致。

3.请求签名值计算过程说明

3.1 拼接待签名字符串

待签名字符串由以下7个字段组合拼接生成:

字段

字段说明

HTTPMethod

HTTP的请求方法,全部大写,例如:GET、POST。

Accept

请求头中 Accept 的值,建议统一为*/*。

Content-Hash

基于SM3算法的请求体body的摘要值(与原有的 Content-MD5 含义一致的 header)

请求头中的 X-Tsign-Content-Hash 值

GET 和 DELETE 请求且Body体无数据时,此参数可为 ""(空字符串)。

Content-Type

请求的与实体对应的MIME信息,例如:application/json; charset=UTF-8

请求头中的 Content-Type 值

GET 和 DELETE 请求且Body体无数据时,此参数可为 ""(空字符串)。

Date

请求头中的 Date 值,可为 ""(空字符串)。时间的描述格式需符合RFC822规范。

例如,Thu, 11 Jul 2015 15:33:24 GMT。

可为 ""(空字符串)。

Headers

用户可以选取指定的Header参与签名,将希望参与签名计算的Key与Value组合拼接成字符串,Key需按字典(ASCII码)升序排序。

可为 ""(空字符串)。

PathAndParameters

e签宝 API 的接口Url(注意不含https://{host}部分)

PathAndParameters 字段包含Path 和 Query 中的所有参数。例如:

/v3/files/123/keyword-positions?keywords=关键字1,关键字2

Query 参数对的Key需按字典(ASCII码)升序排序。

待签名字符串拼接后格式如下所示:

HTTPMethod
Accept
Content-Hash
Content-Type
Date
Headers
PathAndParameters

字段之间使用\n间隔,若Headers为空,则不需要加\n,其他字段若为空都需要保留\n。注:大小写敏感。

待签名字符串组合拼接代码示例:

public static void main(String[] args) {
    
    String HTTPMethod = "POST";
    String Accept = "*/*";
    String contentHash = "OvpuiG1MVYbQCBB00h1jkx8/lFGcFnLnjRkDCIXph5g=";
    String ContentType = "application/json; charset=UTF-8";
    String Date = "";
    String Headers = "";
    String PathAndParameters = "/v3/sign-flow/create-by-file";
    // 组合拼接待签名字符串
    StringBuffer strBuff = new StringBuffer();
    strBuff.append(HTTPMethod).append("\n").append(Accept).append("\n").append(contentHash).append("\n")
        .append(ContentType).append("\n").append(Date).append("\n");
    if ("".equals(Headers)) {
        strBuff.append(Headers).append(PathAndParameters);
    } else {
        strBuff.append(Headers).append("\n").append(PathAndParameters);
    }
    String StringToSign = strBuff.toString();
    System.out.println("待签名字符串:\n" + StringToSign);
    
}
- - -  拼接后示例 - - -
POST
*/*
OvpuiG1MVYbQCBB00h1jkx8/lFGcFnLnjRkDCIXph5g=
application/json; charset=UTF-8

/v3/sign-flow/create-by-file

待签名字符串拼接规则

下面介绍下每个字段的提取规则:

  • HTTPMethod:HTTP的请求方法,全部大写,例如:GET、POST。
  • Accept:请求头中的 Accept 值,建议显式设置 Accept Header。当 Accept 为空时,部分 HTTP 框架在未设置 Accept 请求头时会给 Accept 设置默认值为 */*,建议统一设置为 */*。
  • Content-Hash:基于SM3算法的请求体body的摘要值(与原有的 Content-MD5 含义完全一致的 header),空body时可为 ""(空字符串)
  • Content-Type:请求头中的 Content-Type 值,可为 ""(空字符串)。
  • Date:请求头中的 Date 值,可为 ""(空字符串)。时间的描述格式需符合RFC822规范。例如,Thu, 11 Jul 2015 15:33:24 GMT,点击查看日期时间描述格式说明
  • Headers:开发者选取指定的请求头 Header 参与请求签名计算时,
  • 当开发者需要将一些请求头 Header 也参与请求签名计算时,请求头 Header 的 Key 需按字典(ASCII码)升序排序后再按照下面格式进行组合拼接:

HeaderKey1 + ":" + HeaderValue1 + "\n" + HeaderKey2 + ":" + HeaderValue2 + ...

    • 某个Header的Value为空,则使用 HeaderKey+":"+"\n" 参与签名,需要保留Key和英文冒号;
    • 以下Header不参与Header签名计算:X-Tsign-Open-Ca-SignatureX-Tsign-Open-Ca-Signature-Headers、Accept、Content-Hash、Content-Type、Date。
    • 所有参与签名的Header的Key使用英文逗号进行分割后放到请求头Header的X-Tsign-Open-Ca-Signature-Headers中;
  • PathAndParameters 这个字段包含 Path 和 Query 的所有参数,简单说就是不包含https://{host}部分的e签宝 API 接口Url,Query 参数对的 Key 需按字典(ASCII码)升序排序后再按照下面格式进行组合拼接:

Path + "?" + Key1 + "=" + Value1 + "&" + Key2 + "=" + Value2

    • Query 和Form 参数为空时,则直接使用 Path,不需要添加?;
    • 参数的 Value 为空时只保留 Key 参与签名,=不需要再加入签名;
    • Query 和 Form 存在数组参数时(Key相同,Value不同的参数) ,取第一个 Value 参与签名计算;

3.2 计算请求签名值

点击下载Demo-EsignGatewaySignSM3.zip

开发者使用 AppSecret(应用密钥) 对待签名字符串进行 HmacSM3 加密处理,计算得出请求签名值

计算基于SM3算法的请求body体摘要值

/***
   *
   * @param str 待计算的数据
   * @return 计算请求 body 摘要
   * @throws Exception 加密过程中的异常信息
   */
  public static String doContentHash(String str) throws Exception {
    String algorithm = "SM3";
    byte[] hashBytes = null;
    MessageDigest hash = null;
    String contentHash = null;
    try {
      hash = MessageDigest.getInstance(algorithm);
      hash.reset();
      // 计算摘要
      hash.update(str.getBytes("UTF-8"));
      // 获取body的二进制数组(128位)
      hashBytes = hash.digest();
      // 把摘要的二进制数组hashBytes使用Base64进行编码(而不是对32位的16进制字符串进行编码)
      contentHash = new String(Base64.encodeBase64(hashBytes), "UTF-8");
    } catch (NoSuchAlgorithmException e) {
      String msg = MessageFormat.format("不支持此算法: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    } catch (UnsupportedEncodingException e) {
      String msg = MessageFormat.format("不支持的字符编码: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    }
    return contentHash;
  }

计算请求签名值代码示例

/***
   * 计算请求签名值
   *
   * @param message 待计算的消息
   * @param secret 密钥
   * @return HmacSM3计算后摘要值的Base64编码
   * @throws Exception 加密过程中的异常信息
   */
  public static String doSignatureBase64(String message, String secret) throws Exception {
    String algorithm = "HmacSM3";
    Mac hmacSM3;
    String digestBase64 = null;
    try {
      hmacSM3 = Mac.getInstance(algorithm);
      byte[] keyBytes = secret.getBytes("UTF-8");
      byte[] messageBytes = message.getBytes("UTF-8");
      hmacSM3.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, algorithm));
      // 使用HmacSM3对二进制数据消息Bytes计算摘要
      byte[] digestBytes = hmacSM3.doFinal(messageBytes);
      // 把摘要后的结果digestBytes使用Base64进行编码
      digestBase64 = new String(Base64.encodeBase64(digestBytes), "UTF-8");
    } catch (NoSuchAlgorithmException e) {
      String msg = MessageFormat.format("不支持此算法: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    } catch (UnsupportedEncodingException e) {
      String msg = MessageFormat.format("不支持的字符编码: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    } catch (InvalidKeyException e) {
      String msg = MessageFormat.format("无效的密钥规范: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    }
    return digestBase64;
  }

请求签名鉴权完整代码示例

package esign.gateway.demo;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedHashMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import com.alibaba.fastjson.JSONObject;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class TestSM3 {

  /**
   * 这段代码将 Bouncy Castle 提供程序添加到安全提供程序链中,以便使用 BC HmacSM3 算法。该方法只需要执行一次即可。
   */
  static {
    Security.addProvider(new BouncyCastleProvider());
  }

  public static void main(String[] args) {
    // 应用ID
    String appId = "7438******";//沙箱环境7或4开头,正式环境5开头
    // 应用密钥
    String appKey = "106551******243dbcf";
    // e签宝接口调用域名(模拟环境)
    String host = "https://smlopenapi.esign.cn";
    // e签宝接口调用域名(正式环境)
//  String host = "https://openapi.esign.cn";

    // 请求签名鉴权-POST请求
    //testPost(appId, appKey, host);

    // 请求签名鉴权-GET请求
    String signFlowId = "fdad1*******57789f799";
    testGet(appId, appKey, host, signFlowId);

  }


  /***
   * 请求签名鉴权-POST请求
   *
   * @param appId
   * @param appKey
   * @param host
   */
  public static void testPost(String appId, String appKey, String host) {
    // 计算签名拼接的url
    String postUrl = "/v3/files/file-upload-url";
    // 完整的请求地址
    String postAllUrl = host + postUrl;


    try {
      // 构建请求Body体
      JSONObject reqBodyObj = new JSONObject();
      reqBodyObj.put("contentMd5", "KMYh+0qU9/FDXf2TwCGbeg==");
      reqBodyObj.put("contentType", "application/octet-stream");
      reqBodyObj.put("convertToPDF", "true");
      reqBodyObj.put("fileName", "销售合同.docx");
      reqBodyObj.put("fileSize", "81825");
      //reqBodyObj.put("convertToHTML", "false");

      // 请求Body体数据
      String reqBodyData = reqBodyObj.toString();
      // 对请求Body体内的数据计算摘要Hash
      String contentHash = doContentHash(reqBodyData);
      System.out.println("请求body数据:"+reqBodyData);
      System.out.println("body的hash值:"+ contentHash);

      // 构建待签名字符串
        String method = "POST";
      String accept = "*/*";
      String contentType = "application/json";
      String url = postUrl;
      String date = "";
      String headers = "";

      StringBuffer sb = new StringBuffer();
      sb.append(method).append("\n").append(accept).append("\n").append(contentHash).append("\n")
          .append(contentType).append("\n").append(date).append("\n");
      if ("".equals(headers)) {
        sb.append(headers).append(url);
      } else {
        sb.append(headers).append("\n").append(url);
      }

      // 构建参与请求签名计算的明文
      String plaintext = sb.toString();
      // 计算请求签名值
      String reqSignature = doSignatureBase64(plaintext, appKey);
      System.out.println("计算请求签名值:"+reqSignature);

      // 获取时间戳(精确到毫秒)
      long timeStamp = timeStamp();

      // 构建请求头
      LinkedHashMap<String, String> header = new LinkedHashMap<String, String>();
      header.put("X-Tsign-Open-App-Id", appId);
      header.put("X-Tsign-Open-Auth-Mode", "Signature");
      header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(timeStamp));
      header.put("Accept", accept);
      header.put("Content-Type", contentType);
      header.put("X-Tsign-Open-Ca-Signature", reqSignature);
      header.put("X-Tsign-Content-Hash", contentHash);
      header.put("X-Tsign-Content-Hash-Algorithm", "SM3");
      header.put("X-Tsign-Open-Ca-Signature-Algorithm", "HmacSM3");

      // 发送POST请求
      String result = HTTPHelper.sendPOST(postAllUrl, reqBodyData, header, "UTF-8");
      JSONObject resultObj = JSONObject.parseObject(result);
      System.out.println("请求返回信息: " + resultObj.toString());
    } catch (Exception e) {
      e.printStackTrace();
      String msg = MessageFormat.format("请求签名鉴权方式调用接口出现异常: {0}", e.getMessage());
      System.out.println(msg);
    }
  }

  /***
   * 请求签名鉴权-GET请求
   *
   * @param appId
   * @param appKey
   * @param host
   */

  public static void testGet(String appId, String appKey, String host,String signFlowId) {
    // 计算签名拼接的url
    String getUrl = "/v3/sign-flow/"+signFlowId+"/detail";
    // 完整的请求地址
    String getAllUrl = host + getUrl;

    try {
      // GET请求时contentHash可以为"",或者不计算在内
      String contentHash = "";

      // 构建待签名字符串
      String method = "GET";
      String accept = "*/*";
      String contentType = "application/json; charset=UTF-8";
      String url = getUrl;
      String date = "";
      String headers = "";

      StringBuffer sb = new StringBuffer();
      sb.append(method).append("\n").append(accept).append("\n").append(contentHash).append("\n")
              .append(contentType).append("\n").append(date).append("\n");
      if ("".equals(headers)) {
        sb.append(headers).append(url);
      } else {
        sb.append(headers).append("\n").append(url);
      }

      // 构建参与请求签名计算的明文
      String plaintext = sb.toString();
      // 计算请求签名值
      String reqSignature = doSignatureBase64(plaintext, appKey);
      System.out.println("计算请求签名值:"+ reqSignature);
      // 获取时间戳(精确到毫秒)
      long timeStamp = timeStamp();

      // 构建请求头
      LinkedHashMap<String, String> header = new LinkedHashMap<String, String>();
      header.put("X-Tsign-Open-App-Id", appId);
      header.put("X-Tsign-Open-Auth-Mode", "Signature");
      header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(timeStamp));
      header.put("Accept", accept);
      header.put("Content-Type", contentType);
      header.put("X-Tsign-Open-Ca-Signature", reqSignature);
      header.put("X-Tsign-Content-Hash", contentHash);
      header.put("X-Tsign-Content-Hash-Algorithm", "SM3");
      header.put("X-Tsign-Open-Ca-Signature-Algorithm", "HmacSM3");

      HashMap<String, Object> query = new HashMap<String, Object>();
     // query.put("orgIDCardNum", "9100*****0004");
     // query.put("orgIDCardType", "CRED_ORG_USCC");


      // 发送POST请求
      String result = HTTPHelper.sendGet(getAllUrl, query, header, "UTF-8");
      JSONObject resultObj = JSONObject.parseObject(result);
      System.out.println("请求返回信息: " + resultObj.toString());
    } catch (Exception e) {
      e.printStackTrace();
      String msg = MessageFormat.format("请求签名鉴权方式调用接口出现异常: {0}", e.getMessage());
      System.out.println(msg);
    }
  }



  /***
   *
   * @param str 待计算的数据
   * @return 计算请求 body 摘要
   * @throws Exception 加密过程中的异常信息
   */
  public static String doContentHash(String str) throws Exception {
    String algorithm = "SM3";
    byte[] hashBytes = null;
    MessageDigest hash = null;
    String contentHash = null;
    try {
      hash = MessageDigest.getInstance(algorithm);
      hash.reset();
      // 计算摘要
      hash.update(str.getBytes("UTF-8"));
      // 获取body的二进制数组(128位)
      hashBytes = hash.digest();
      // 把摘要的二进制数组hashBytes使用Base64进行编码(而不是对32位的16进制字符串进行编码)
      contentHash = new String(Base64.encodeBase64(hashBytes), "UTF-8");
    } catch (NoSuchAlgorithmException e) {
      String msg = MessageFormat.format("不支持此算法: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    } catch (UnsupportedEncodingException e) {
      String msg = MessageFormat.format("不支持的字符编码: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    }
    return contentHash;
  }

  /***
   * 计算请求签名值
   *
   * @param message 待计算的消息
   * @param secret 密钥
   * @return HmacSM3计算后摘要值的Base64编码
   * @throws Exception 加密过程中的异常信息
   */
  public static String doSignatureBase64(String message, String secret) throws Exception {
    String algorithm = "HmacSM3";
    Mac hmacSM3;
    String digestBase64 = null;
    try {
      hmacSM3 = Mac.getInstance(algorithm);
      byte[] keyBytes = secret.getBytes("UTF-8");
      byte[] messageBytes = message.getBytes("UTF-8");
      hmacSM3.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, algorithm));
      // 使用HmacSM3对二进制数据消息Bytes计算摘要
      byte[] digestBytes = hmacSM3.doFinal(messageBytes);
      // 把摘要后的结果digestBytes使用Base64进行编码
      digestBase64 = new String(Base64.encodeBase64(digestBytes), "UTF-8");
    } catch (NoSuchAlgorithmException e) {
      String msg = MessageFormat.format("不支持此算法: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    } catch (UnsupportedEncodingException e) {
      String msg = MessageFormat.format("不支持的字符编码: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    } catch (InvalidKeyException e) {
      String msg = MessageFormat.format("无效的密钥规范: {0}", e.getMessage());
      Exception ex = new Exception(msg);
      ex.initCause(e);
      throw ex;
    }
    return digestBase64;
  }

  /***
   * 获取时间戳(毫秒级)
   *
   * @return 毫秒级时间戳,如 1578446909000
   */
  public static long timeStamp() {
    long timeStamp = System.currentTimeMillis();
    return timeStamp;
  }

}

HTTP网络请求代码示例

package esign.gateway.demo;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HTTPHelper {
  // slf4j日志记录器
  private static final Logger LOG = LoggerFactory.getLogger(HTTPHelper.class);

  /***
   * 向指定URL发送GET方法的请求
   *
   * @param apiUrl
   * @param data
   * @param projectId
   * @param signature
   * @param encoding
   * @return
   * @throws Exception
   */
  public static String sendGet(String apiUrl, HashMap<String, Object> param,
      LinkedHashMap<String, String> headers, String encoding) throws Exception {
    // 获得响应内容
    String http_RespContent = null;
    HttpURLConnection httpURLConnection = null;
    int http_StatusCode = 0;
    String http_RespMessage = null;
    try {
      // 实际请求完整Url
      StringBuffer fullUrl = new StringBuffer();
      if (null != param) {
        if (0 != param.size()) {
          StringBuffer params = new StringBuffer();
          for (Entry<String, Object> entry : param.entrySet()) {
            params.append(entry.getKey());
            params.append("=");
            params.append(entry.getValue().toString());
            params.append("&");
          }
          if (params.length() > 0) {
            params.deleteCharAt(params.lastIndexOf("&"));
          }
          fullUrl.append(apiUrl).append((params.length() > 0) ? "?" + params.toString() : "");
        } else {
          fullUrl.append(apiUrl);
        }
      } else {
        fullUrl.append(apiUrl);
      }

      LOG.info(">>>> 实际请求Url: " + fullUrl.toString());

      // 建立连接
      URL url = new URL(fullUrl.toString());
      httpURLConnection = (HttpURLConnection) url.openConnection();
      // 需要输出
      httpURLConnection.setDoOutput(true);
      // 需要输入
      httpURLConnection.setDoInput(true);
      // 不允许缓存
      httpURLConnection.setUseCaches(false);
      // HTTP请求方式
      httpURLConnection.setRequestMethod("GET");
      // 设置Headers
      if (null != headers) {
        for (String key : headers.keySet()) {
          httpURLConnection.setRequestProperty(key, headers.get(key));
        }
      }
      // 连接会话
      httpURLConnection.connect();
      // 获得响应状态(HTTP状态码)
      http_StatusCode = httpURLConnection.getResponseCode();
      // 获得响应消息(HTTP状态码描述)
      http_RespMessage = httpURLConnection.getResponseMessage();
      // 获得响应内容
      if (HttpURLConnection.HTTP_OK == http_StatusCode) {
        // 返回响应结果
        http_RespContent = getResponseContent(httpURLConnection);
      } else {
        // 返回非200状态时响应结果
        http_RespContent = getErrorResponseContent(httpURLConnection);
        String msg =
            MessageFormat.format("请求失败: Http状态码 = {0} , {1}", http_StatusCode, http_RespMessage);
        LOG.info(msg);
      }
    } catch (UnknownHostException e) {
      String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } catch (MalformedURLException e) {
      String message = MessageFormat.format("格式错误的URL: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } catch (IOException e) {
      String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } catch (Exception e) {
      String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } finally {
      if (null != httpURLConnection) {
        httpURLConnection.disconnect();
      }
    }
    return http_RespContent;
  }

  /***
   * 向指定URL发送POST方法的请求
   *
   * @param apiUrl
   * @param data
   * @param projectId
   * @param signature
   * @param encoding
   * @return
   * @throws Exception
   */
  public static String sendPOST(String apiUrl, String data, LinkedHashMap<String, String> headers,
      String encoding) throws Exception {
    // 获得响应内容
    String http_RespContent = null;
    HttpURLConnection httpURLConnection = null;
    int http_StatusCode = 0;
    String http_RespMessage = null;
    try {
      // 建立连接
      URL url = new URL(apiUrl);
      httpURLConnection = (HttpURLConnection) url.openConnection();
      // 需要输出
      httpURLConnection.setDoOutput(true);
      // 需要输入
      httpURLConnection.setDoInput(true);
      // 不允许缓存
      httpURLConnection.setUseCaches(false);
      // HTTP请求方式
      httpURLConnection.setRequestMethod("POST");
      // 设置Headers
      if (null != headers) {
        for (String key : headers.keySet()) {
          httpURLConnection.setRequestProperty(key, headers.get(key));
        }
      }
      // 连接会话
      httpURLConnection.connect();
      // 建立输入流,向指向的URL传入参数
      DataOutputStream dos = new DataOutputStream(httpURLConnection.getOutputStream());
      // 设置请求参数
      dos.write(data.getBytes(encoding));
      dos.flush();
      dos.close();
      // 获得响应状态(HTTP状态码)
      http_StatusCode = httpURLConnection.getResponseCode();
      // 获得响应消息(HTTP状态码描述)
      http_RespMessage = httpURLConnection.getResponseMessage();
      // 获得响应内容
      if (HttpURLConnection.HTTP_OK == http_StatusCode) {
        // 返回响应结果
        http_RespContent = getResponseContent(httpURLConnection);
      } else {
        // 返回非200状态时响应结果
        http_RespContent = getErrorResponseContent(httpURLConnection);
        String msg =
            MessageFormat.format("请求失败: Http状态码 = {0} , {1}", http_StatusCode, http_RespMessage);
        LOG.info(msg);
      }
    } catch (UnknownHostException e) {
      String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } catch (MalformedURLException e) {
      String message = MessageFormat.format("格式错误的URL: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } catch (IOException e) {
      String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } catch (Exception e) {
      String message = MessageFormat.format("网络请求时发生异常: {0}", e.getMessage());
      Exception ex = new Exception(message);
      ex.initCause(e);
      throw ex;
    } finally {
      if (null != httpURLConnection) {
        httpURLConnection.disconnect();
      }
    }
    return http_RespContent;
  }

  /***
   * 读取HttpResponse响应内容
   *
   * @param httpURLConnection
   * @return
   * @throws UnsupportedEncodingException
   * @throws IOException
   */
  private static String getResponseContent(HttpURLConnection httpURLConnection)
      throws UnsupportedEncodingException, IOException {
    StringBuffer contentBuffer = null;
    BufferedReader responseReader = null;
    try {
      contentBuffer = new StringBuffer();
      String readLine = null;
      responseReader =
          new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "UTF-8"));
      while ((readLine = responseReader.readLine()) != null) {
        contentBuffer.append(readLine);
      }
    } finally {
      if (null != responseReader) {
        responseReader.close();
      }
    }
    return contentBuffer.toString();
  }

  /***
   * 读取HttpResponse响应内容
   *
   * @param httpURLConnection
   * @return
   * @throws UnsupportedEncodingException
   * @throws IOException
   */
  private static String getErrorResponseContent(HttpURLConnection httpURLConnection)
      throws UnsupportedEncodingException, IOException {
    StringBuffer contentBuffer = null;
    BufferedReader responseReader = null;
    try {
      contentBuffer = new StringBuffer();
      String readLine = null;
      responseReader =
          new BufferedReader(new InputStreamReader(httpURLConnection.getErrorStream(), "UTF-8"));
      while ((readLine = responseReader.readLine()) != null) {
        contentBuffer.append(readLine);
      }
    } finally {
      if (null != responseReader) {
        responseReader.close();
      }
    }
    return contentBuffer.toString();
  }
}


我要纠错