
# C2 监控/系统外呼/话单/知识库类接口鉴权方式
#### AK/SK获取方式
**AK/SK认证**
AK/SK认证就是使用AK/SK对请求进行签名，在请求时将签名信息添加到消息头，从而通过身份认证。
- AK（Access Key ID）：访问密钥ID。与私有访问密钥关联的唯一标识符；访问密钥ID和私有访问密钥一起使用，对请求进行加密签名。
  
- SK（Secret Access Key）：与访问密钥ID结合使用的密钥，对请求进行加密签名，可标识发送方，并防止请求被修改。
  
使用AK/SK认证时，您可以基于签名算法使用AK/SK对请求进行签名。
**私有AK、SK获取方式**
- 集成环境：请联系运营人员获取。
![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/note_3.0-zh-cn.png)
接口鉴权如果通过 ，则https响应码为200；如果不通过，则https的响应码为401。
#### 鉴权算法
![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/notice_3.0-zh-cn.png)
下面以CC-CMS接口为例介绍鉴权算法，其余容器化网元CC-iKBS、虚拟化网元CC-FS、CC-iSales的鉴权算法可直接参考。
所有接口服务器端都会对客户端的请求数据做鉴权签名验证，鉴权流程如下:
![](https://support.huaweicloud.com/api-ccfs-cec/figure/zh-cn_image_0000001833065933.png "点击放大")
调用者生成Authorization的生成规则内容如下：
![](https://support.huaweicloud.com/api-ccfs-cec/figure/zh-cn_image_0000001833185865.png "点击放大")
各内容的算法和规则
- **CmsParameters**
  ![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/note_3.0-zh-cn.png)
  该参数类为CC-CMS二次开发demo中的参数汇总，在接下来的描述、调用中，会直接取该类中的参数。使用者只需统一修改该参数类，即可完成相应的鉴权配置。
  以下是demo中基本的参数样例，调用者可根据具体情况，增加相应的参数，以方便后续的项目管理：
  ```
  /**
   * CC-CMS接口请求参数类定义
   */
  public class CmsParameters
  {
  //以下信息需要根据实际信息替换
  /**
   * host访问地址
   */
  public static final String host = "10.22.26.181:28080";
  public static final String url = "https://" + host;
  /**
   * 开发者ID，即AK
   */
  public static final String accessKey= "globalaktest";
  /**
   * 开发者秘钥（未加密）,即SK
   */
  public static final String secretKey = "1q************20";
  }
  ```
  
- **SignInfo**
  ![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/note_3.0-zh-cn.png)
  该类主要包含一些固定HEAD的定义，以及一些用于构造CanonicalRequest和Signature的基本函数。
  
```
package com.huawei.client.rest.v2.demo.sign;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
@Setter
@Getter
public class SignInfo
{
public static final String HEAD_AUTHORIZATION = "authorization";
public static final String HEAD_HOST = "host";
public static final String HEAD_CONTENT_LENGTH = "Content-Length";
public static final String HEAD_CONTENT_TYPE = "Content-Type";
public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
// 默认值，不用设置
private String authVersion = "auth-v2";
private String httpMethod;
private String uri;
// 当前服务侧暂无GET支持
private Map<String, String> queryParameters;
private Map<String, String> signedHeaders;
private String payload;
private String accessKey;
private String secretKey;
private Date timestamp;
public String authString() throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException
{
String authStringPrefix = this.authStringPrefix();
String signingKey = SignerUtils.sha256Hex(this.getSecretKey(), authStringPrefix);
String canonicalRequest = this.canonicalRequest();
String signature = SignerUtils.sha256Hex(signingKey, canonicalRequest);
String authString = authStringPrefix + '/' + signature;
return authString;
}
public String authStringPrefix()
{
StringBuilder buffer = new StringBuilder();
buffer.append(this.authVersion);
buffer.append('/').append(this.accessKey);
buffer.append('/').append(this.formatTimestamp());
buffer.append('/');
this.appendSignedHeaders(buffer);
return buffer.toString();
}
public String canonicalRequest()
{
StringBuilder buffer = new StringBuilder();
buffer.append(this.httpMethod).append('\n');
buffer.append(this.uri).append('\n');
if (this.isNotEmpty(this.queryParameters))
{
this.appendCanonicalQueryString(buffer);
buffer.append('\n');
}
this.appendSignedHeaders(buffer);
buffer.append('\n');
this.appendCanonicalHeaders(buffer);
buffer.append('\n');
if (this.isNotEmpty(this.payload))
{
buffer.append(PathUtils.normalize(this.payload));
}
return buffer.toString();
}
private String appendSignedHeaders(StringBuilder buffer)
{
int start = buffer.length();
Set<String> headerNames = new TreeSet<>(this.signedHeaders.keySet());
for (String name : headerNames)
{
buffer.append(name.toLowerCase(Locale.ENGLISH)).append(';');
}
buffer.deleteCharAt(buffer.length() - 1);
int end = buffer.length();
String signedHeadersStr = buffer.substring(start, end);
return signedHeadersStr;
}
private String appendCanonicalHeaders(StringBuilder buffer)
{
int start = buffer.length();
Set<String> headers = new TreeSet<>();
for (Map.Entry<String, String> entry : this.signedHeaders.entrySet())
{
String header = PathUtils.normalize(entry.getKey()) + ':' 
                        + PathUtils.normalize(entry.getValue());
headers.add(header);
}
for (String header : headers)
{
buffer.append(header).append('\n');
}
buffer.deleteCharAt(buffer.length() - 1);
int end = buffer.length();
String canonicalHeadersStr = buffer.substring(start, end);
return canonicalHeadersStr;
}
private void appendCanonicalQueryString(StringBuilder buffer)
{
// 编码并排序
Set<String> sortedSet = new TreeSet<>();
for (Map.Entry<String, String> e : this.queryParameters.entrySet())
{
String uriEncodeKey = PathUtils.normalize(e.getKey());
String uriEncodeValue = this.isNotEmpty(e.getValue()) ? PathUtils.normalize(e.getValue()) : "";
sortedSet.add(uriEncodeKey + "=" + uriEncodeValue);
}
for (String e : sortedSet)
{
buffer.append(e).append('&');
}
buffer.deleteCharAt(buffer.length() - 1);
}
private String formatTimestamp()
{
SimpleDateFormat format = new SimpleDateFormat(SignInfo.TIMESTAMP_FORMAT);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
return format.format(this.timestamp);
}
private boolean isNotEmpty(String str)
{
if ((null == str) || str.isEmpty())
{
return false;
}
return true;
}
private <K, V> boolean isNotEmpty(Map<K, V> map)
{
if ((null == map) || map.isEmpty())
{
return false;
}
return true;
}
}
```
其中，SignInfo类用到的PathUtils工具类如下：
```
package com.huawei.client.rest.v2.demo.utils;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.BitSet;
public class PathUtils {
private static final String CHARSET = "UTF-8";
private static BitSet URI_UNRESERVED_CHARACTERS = new BitSet();
private static String[] PERCENT_ENCODED_STRINGS = new String[256];
static {
for (int i = 97; i <= 122; i++) {
PathUtils.URI_UNRESERVED_CHARACTERS.set(i);
}
for (int i = 65; i <= 90; i++) {
PathUtils.URI_UNRESERVED_CHARACTERS.set(i);
}
for (int i = 48; i <= 57; i++) {
PathUtils.URI_UNRESERVED_CHARACTERS.set(i);
}
PathUtils.URI_UNRESERVED_CHARACTERS.set(45);
PathUtils.URI_UNRESERVED_CHARACTERS.set(46);
PathUtils.URI_UNRESERVED_CHARACTERS.set(95);
PathUtils.URI_UNRESERVED_CHARACTERS.set(126);
for (int i = 0; i < PathUtils.PERCENT_ENCODED_STRINGS.length; i++) {
PathUtils.PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", new Object[] {Integer.valueOf(i)});
}
}
public static String normalizePath(String path) {
return PathUtils.normalize(path).replace("%2F", "/");
}
public static String normalize(String value) {
try {
StringBuilder builder = new StringBuilder();
for (byte b : value.getBytes(PathUtils.CHARSET)) {
if (PathUtils.URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
builder.append((char) b);
} else {
builder.append(PathUtils.PERCENT_ENCODED_STRINGS[(b & 0xFF)]);
}
}
return builder.toString();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
 * URL 归一化处理
 *
 * @param url
 * @return
 */
public static String normalizeURL(String url) {
try {
return URLDecoder.decode(new URI(url).normalize().toString(), PathUtils.CHARSET);
} catch (URISyntaxException | UnsupportedEncodingException e) {
return url;
}
}
public static void main(String[] args)
{
System.out.println(PathUtils.normalize("123%456"));
}
}
```
- **SignedHeaders**
  ![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/note_3.0-zh-cn.png)
  调用者对HTTPS请求中的Header部分进行选择性编码，也可以自行决定哪些Header参与编码，唯一要求是Host域必须被编码，但headname="Authorization"的head不能参与编码计算，因为Authorization是最终鉴权head域。
  备注：Host的值为对应服务的https地址中的ip:port 。
  大多数情况下，我们推荐对以下Header进行编码：
  ```
  Host="10.22.26.181:28080"  
  Content-Length="22"
  Content-Type="application/json;charset=UTF-8"
  ```
  - 内容计算规则： 遍历参与编码的HttpHead里面的Header name
    1. 把Header name都改为小写，即调用*lowerCase()函数*；
    
    2. 把上面Header name转换后的字符后面追加分割符";"，生成一条记录，注意最后一个字段不追加";"；
    
    3. 把上面所有记录按照字典排序，然后按照顺序连接成一个大字符串。
     
  
  - 代码实现：
    1. 构建signedHeaders的Map，填充所需字段信息，然后调用lowerCaseSignedHeaders函数进行字符处理：
       ```
       // 参与签名鉴权的head字段，host字段是必须的
       Map<String, String> signedHeaders = new HashMap<>();
       signedHeaders.put(SignInfo.HEAD_HOST, CmsParameters.host);
       signedHeaders.put(SignInfo.HEAD_CONTENT_LENGTH, String.valueOf(bodyJson.getBytes("UTF-8").length));
       signedHeaders.put(SignInfo.HEAD_CONTENT_TYPE, "application/json;charset=UTF-8");
       // 鉴权数据接口设置
       SignInfo signInfo = new SignInfo();
       signInfo.setAccessKey(CmsParameters.accessKey);
       signInfo.setSecretKey(CmsParameters.secretKey);
       signInfo.setPayload(bodyJson);
       signInfo.setTimestamp(new Date());
       signInfo.setHttpMethod(HttpProxyHelper.HTTP_METHOD_POST);
       signInfo.setUri(httpPath);
       signInfo.setSignedHeaders(lowerCaseSignedHeaders(signedHeaders));
       ```
       以上函数的完整代码参见：[如何调通第一个接口]
       
    
    2. 函数：lowerCaseSignedHeaders的具体实现：
       ```
       /**
        * 参与计算的signedHeaders进行转换
        * @param
        * @return Map<String, String>
        */
       private Map<String, String> lowerCaseSignedHeaders(Map<String, String> signedHeaders)
       {
       if ((null == signedHeaders) || signedHeaders.isEmpty())
       {
       throw new IllegalArgumentException("signedHeaders cann't be null.");
       }
       Map<String, String> headers = new HashMap<>();
       for (Entry<String, String> e : signedHeaders.entrySet())
       {
       String name = e.getKey();
       String value = e.getValue();
       headers.put(name.toLowerCase(Locale.ENGLISH), value.trim());
       }
       if (!signedHeaders.containsKey(SignInfo.HEAD_HOST))
       {
       throw new IllegalArgumentException("signedHeaders must has host.");
       }
       return headers;
       }
       ```
       
     
  
  - 具体例子：
    ```
    例子：参与编码的HttpHead如下:
    Host="10.22.26.181:28080"  
    Content-Length="22"
    Content-Type="application/json;charset=UTF-8"
    经过上述规则处理后为：
    SignedHeaders=content-length;content-type;host
    ```
    
   
- **CanonicalHeaders**
  ![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/note_3.0-zh-cn.png)
  其要求计算的编码规则与SignedHeaders一致，但增加了head value的编码。
  - 内容计算规则： 遍历参与编码的HttpHead里面的head name
    1. 把Header name都改为小写，即调用*lowerCase()函数*；
    
    2. 调用NormalizePath函数，对刚才转换后的小写字符串进行格式化；
    
    3. 格式化后的字符串+":"+NormalizePath((Header value).trim())，生成一条记录字符串；
    
    4. 把上面的记录按照字典排序，进行排序；
    
    5. 遍历排序后的记录，中间追加字符串"\\n"连接成一个大的字符串：
    
    
    最后一条记录不追加"\\n"。
    ```
    例子：参与编码的HttpHead如下:
    Host="10.22.26.181:28080"  
    Content-Length="22"
    Content-Type="application/json;charset=UTF-8"
    经过上述规则处理后CanonicalHeaders为：
    content-length:22\n
    content-type:application%2Fjson%3Bcharset%3DUTF-8\n
    host:10.22.26.181%3A28080
    ```
    
   
- **CanonicalRequest**
  - 内容计算规则：
    ```
    CanonicalRequest = $HttpMethod + "\n" + $HttpURI+ "\n" + $HttpParameters + "\n" + SignedHeaders($HttpHeaders) + "\n" + CanonicalHeaders ($HttpHeaders) + "\n" + NormalizePath($HttpBody)
    ```
    参数描述：
    1. $HttpMethod，指HTTPS协议中定义的GET、PUT、POST等请求，必须使用全大写的形式。所涉及的HTTP Method有：GET、POST、PUT、DELETE、HEAD，但当前CC-CMS服务仅支持POST这1种；
    
    2. $HttpURI，指接口请求的https URI，比如完整url为https://10.22.26.181:28080/rest/cmsapp/v1/ping那么HttpURI就为/rest/cmsapp/v1/ping，必须以"/"开头，不以"/"开头的需要补充上，空路径为"/"；
    
    3. $HttpParameters，指接口请求URI后面的请求参数，比如https://10.22.26.181:28080/rest/cmsapp/v1/ping?id=123\&name=test，那么HttpParameters就为id=123\&name=test，CC-CMS接口目前没有该参数。
    
    4. $HttpBody：是通过HTTPS BODY体提交到服务器端的字符串，该字符串的形式为标准JSON串，具体字段见各接口的定义。
     
  
  - 代码实现： 参见**[•SignInfo]**中的canonicalRequest()函数。
    
   
- **生成认证字符串Authorization**
  - 内容计算规则：
    1. 首先生成authStringPrefix和SigningKey
       1. 关于authStringPrefix和SigningKey的生成规则：
          ```
          authStringPrefix="auth-v2/{accessKey}/{timestamp}/{SignedHeaders}";
          SigningKey = sha256Hex(secretKey, authStringPrefix);
          ```
          说明：
          auth-v2：鉴权版本号，当期版本为固定字符串"auth-v2"；
          accessKey：调用者的鉴权ID，即AK；
          secretKey：调用者的鉴权密钥，即SK；
          timestamp：调用者端生成的UTC时间，时间字符串格式化为"yyyy-MM-dd'T'HH:mm:ss.SSS'Z"；
          
       
       2. 代码实现：authStringPrefix 生成authStringPrefix的函数在**SignInfo**类的如下两个函数中实现
          ```
          authStringPrefix()
          appendSignedHeaders(StringBuilder buffer)
          ```
          
       
       3. 代码实现：SigningKey：
          参见**SignInfo**类的authString()中，
          ```
          String signingKey = SignerUtils.sha256Hex(this.getSecretKey(), authStringPrefix);
          ```
          其中的sha256Hex()加密算法：参见如下工具类
          ```
          package com.huawei.client.rest.v2.demo.utils;
          import java.io.UnsupportedEncodingException;
          import java.security.InvalidKeyException;
          import java.security.NoSuchAlgorithmException;
          import javax.crypto.Mac;
          import javax.crypto.spec.SecretKeySpec;
          /**
           * Rest接口认证摘要算法工具类
           */
          public class SignerUtils {
          
          private static final String CHARSET = "UTF-8";
          private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
          
          /**
           * 摘要算法
           * @param key
           * @param toSigned
           * @return String
           * @throws NoSuchAlgorithmException
           * @throws InvalidKeyException
           * @throws UnsupportedEncodingException
           */
          public static String sha256Hex(String key, String toSigned) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
          Mac mac = Mac.getInstance("HmacSHA256");
          mac.init(new SecretKeySpec(key.getBytes(SignerUtils.CHARSET), "HmacSHA256"));
          
          String digit = new String(SignerUtils.encodeHex(mac.doFinal(toSigned.getBytes(SignerUtils.CHARSET))));
          return digit;
          }
          
          private static char[] encodeHex(final byte[] data) {
          final int l = data.length;
          final char[] out = new char[l << 1];
          for (int i = 0, j = 0; i < l; i++) {
          out[j++] = SignerUtils.DIGITS_LOWER[(0xF0 & data[i]) >>> 4];
          out[j++] = SignerUtils.DIGITS_LOWER[0x0F & data[i]];
          }
          return out;
          }
          }
          ```
          
        
    
    
    
    2. 生成签名Signature
       1. 签名的生成规则：
          ```
          Signature = sha256Hex(SigningKey, CanonicalRequest)
          ```
          
       
       2. 代码实现：包含加密函数：sha256Hex 参见上述：[代码实现]SigningKey中的sha256Hex()实现方法。
          
        
    
    3. 认证字符串
       1. 认证字符串的生成规则：
          ```
          Authorization:$authStringPrefix/$Signature
          ```
          
       
       2. 代码实现：其中，signedHeaders为上述**signedHeaders** 模块中构建的Map。
          ```
          // 生成签名
          String signature = signInfo.authString();
          Map<String, String> httpHeads = new HashMap<>(signedHeaders);
          // 追加到HTTPHEAD，发送到服务器端
          httpHeads.put(SignInfo.HEAD_AUTHORIZATION, signature);
          ```
          
        
    
    
    ![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/notice_3.0-zh-cn.png)
    认证字符串要通过HttpHead的"Authorization"域，随请求发送给CC-CMS服务端。
    
   
- 完整例子
  1. 如何调通第一个接口：
     ![](https://support.huaweicloud.com/api-ccfs-cec/public_sys-resources/note_3.0-zh-cn.png)
     调通一个接口，鉴权是必要的一环。
     在该项目中，鉴权入口在于buildSignHttpHead()函数，该函数构造了signedHeaders的Map，并将改Map传入SignInfo中；
     SignInfo类中的函数authString() ，揭示了构造认证字符串的主要过程，生成signature签名；
     而认证字符串的关键组成部分，则为签名。其余的信息拼接函数，也在SignInfo类中体现。
     定义请求的BaseRequest：
     ```
     package com.huawei.client.rest.v2.demo.base;
     @Setter
     @Getter
     public class BaseRequest
     {
     private RequestHeader request;
     private Object msgBody;
     public BaseRequest(Object body)
     {
     this.request = new RequestHeader();
     this.msgBody = body;
     }
     public BaseRequest(RequestHeader head, Object body)
     {
     this.request = head;
     this.msgBody = body;
     }
     public RequestHeader getRequest()
     {
     return this.request;
     }
     public void setRequest(RequestHeader request)
     {
     this.request = request;
     }
     public Object getMsgBody()
     {
     return this.msgBody;
     }
     public void setMsgBody(Object msgBody)
     {
     this.msgBody = msgBody;
     }
     }
     ```
     其中，RequestHeader如下：
     ```
     package com.huawei.client.rest.v2.demo.base;
     public class RequestHeader
     {
     /**
      * 版本号
      */
     private String version = "2.0";
     
     public String getVersion()
     {
     return this.version;
     }
     
     public void setVersion(String version)
     {
     this.version = version;
     }
     }
     ```
     定义请求的BaseResponse：
     ```
     package com.huawei.client.rest.v2.demo.base;
     @Setter
     @Getter
     public class BaseResponse
     {
     final static int SUCCESS = 0;
     private ResponseHead resultHead;
     private Object resultData;
     public BaseResponse()
     {
     }
     public BaseResponse(String resultCode, String resultMsg, Object resultData)
     {
     this.resultHead = new ResponseHead(resultCode, resultMsg);
     this.resultData = resultData;
     }
     public BaseResponse(ResponseHead resultHead, Object resultData)
     {
     this.resultHead = resultHead;
     this.resultData = resultData;
     }
     }
     ```
     其中，ResponseHead如下：
     ```
     package com.huawei.client.rest.v2.demo.base;
     public class ResponseHead
     {
     private String resultCode;
     /**
      * 响应结果
      */
     private String resultMsg;
     public ResponseHead()
     {
     }
     public ResponseHead(String resultCode, String resultMsg)
     {
     this.resultCode = resultCode;
     this.resultMsg = resultMsg;
     }
     public String getResultCode()
     {
     return this.resultCode;
     }
     public void setResultCode(String resultCode)
     {
     this.resultCode = resultCode;
     }
     public String getResultMsg()
     {
     return this.resultMsg;
     }
     public void setResultMsg(String resultMsg)
     {
     this.resultMsg = resultMsg;
     }
     }
     ```
     buildSignHttpHead()函数的具体实现参见ToolUtils：
     ```
     package com.huawei.client.rest.v2.demo.utils;
     import java.io.BufferedReader;
     import java.io.File;
     import java.io.FileOutputStream;
     import java.io.FileReader;
     import java.io.InputStream;
     import java.io.UnsupportedEncodingException;
     import java.security.InvalidKeyException;
     import java.security.NoSuchAlgorithmException;
     import java.text.SimpleDateFormat;
     import java.util.ArrayList;
     import java.util.Date;
     import java.util.HashMap;
     import java.util.List;
     import java.util.Locale;
     import java.util.Map;
     import com.alibaba.fastjson.JSONObject;
     import com.huawei.client.rest.v2.demo.HttpProxyHelper;
     import com.huawei.client.rest.v2.demo.base.BaseRequest;
     import com.huawei.client.rest.v2.demo.config.CmsParameters;
     import com.huawei.client.rest.v2.demo.sign.SignInfo;
     /**
      * Rest接口认证摘要算法工具类
      */
     public class ToolUtils {
     /**
      * 由字符串转换成文件工具
      * @param in
      * @param filePath
      * @param fileName
      * @return
      */
     public static boolean saveToFileByStr(InputStream in, String filePath, String fileName){
     boolean flag = true;
     if(in != null){
     try {
     // 将上面生成的文件格式字符串 fileStr，还原成文件显示
     File file=new File(filePath,fileName);
     FileOutputStream fos=new FileOutputStream(file);
     try{
     byte[] buffer = new byte[1024];
     int reader = 0;
     while ((reader = in.read(buffer)) != -1) {
     fos.write(buffer, 0, reader);
     }
     fos.flush();
     }
     finally {
     fos.close();
     in.close();
     }
     } catch (Exception e) {
     flag = false;
     e.printStackTrace();
     }
     }
     return flag;
     }
     /**
      * 传入.csv文件路径
      * 返回读取到的原始录音文件地址工具
      * @param filePath
      * @return
      */
     public static List<String> readFile(String filePath){
     List<String> originalFileList = new ArrayList<String>();
     try {
     BufferedReader reader = new BufferedReader(new FileReader(filePath));
     //第一行为表头信息
     reader.readLine();
     String line = null;
     while((line=reader.readLine())!=null){
     String item[] = line.split(",");//CSV格式文件为逗号分隔符文件，这里根据逗号切分
     originalFileList.add(item[item.length-4]);
     }
     } catch (Exception e) {
     e.printStackTrace();
     }
     return originalFileList;
     }
     /**
      * 实际客户端开发根据JSON框架直接转换BaseRequest为Json串
      * @param request
      * @return String
      * @throws Exception
      */
     public static String toJsonString(BaseRequest request) throws Exception
     {
     JSONObject jsonObject = new JSONObject();
     StringBuilder buffer = new StringBuilder();
     buffer.append("{");
     buffer.append("\"request\":");
     buffer.append(jsonObject.toJSONString(request.getRequest()));
     buffer.append(",");
     buffer.append("\"msgBody\":");
     buffer.append(jsonObject.toJSONString(request.getMsgBody()));
     buffer.append("}");
     return buffer.toString();
     }
     /**
      * 构造鉴权字段信息
      * @param httpPath
      * @param bodyJson
      * @return
      * @throws InvalidKeyException
      * @throws NoSuchAlgorithmException
      * @throws UnsupportedEncodingException
      */
     public Map<String, String> buildSignHttpHead(String httpPath, String bodyJson)
     throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException
     {
     // 参与签名鉴权的head字段，host字段是必须的
     Map<String, String> signedHeaders = new HashMap<>();
     signedHeaders.put(SignInfo.HEAD_HOST, CmsParameters.host);
     signedHeaders.put(SignInfo.HEAD_CONTENT_LENGTH, String.valueOf(bodyJson.getBytes("UTF-8").length));
     signedHeaders.put(SignInfo.HEAD_CONTENT_TYPE, "application/json;charset=UTF-8");
     // 鉴权数据接口设置
     SignInfo signInfo = new SignInfo();
     signInfo.setAccessKey(CmsParameters.accessKey);
     signInfo.setSecretKey(CmsParameters.secretKey);
     signInfo.setPayload(bodyJson);
     signInfo.setTimestamp(new Date());
     signInfo.setHttpMethod(HttpProxyHelper.HTTP_METHOD_POST);
     signInfo.setUri(httpPath);
     signInfo.setSignedHeaders(this.lowerCaseSignedHeaders(signedHeaders));
     // 生成签名
     String signature = signInfo.authString();
     Map<String, String> httpHeads = new HashMap<>(signedHeaders);
     // 追加到HTTPHEAD，发送到服务器端
     httpHeads.put(SignInfo.HEAD_AUTHORIZATION, signature);
     return httpHeads;
     }
     /**
      * 参与计算的signedHeaders进行转换
      * @param
      * @return Map<String, String>
      */
     private Map<String, String> lowerCaseSignedHeaders(Map<String, String> signedHeaders)
     {
     if ((null == signedHeaders) || signedHeaders.isEmpty())
     {
     throw new IllegalArgumentException("signedHeaders cann't be null.");
     }
     Map<String, String> headers = new HashMap<>();
     for (Entry<String, String> e : signedHeaders.entrySet())
     {
     String name = e.getKey();
     String value = e.getValue();
     headers.put(name.toLowerCase(Locale.ENGLISH), value.trim());
     }
     if (!signedHeaders.containsKey(SignInfo.HEAD_HOST))
     {
     throw new IllegalArgumentException("signedHeaders must has host.");
     }
     return headers;
     }
     }
     ```
     
  
  2. 代码实现：
     - 建立工程的main函数：
       ```
       public static void main(String[] args)
       {
       HttpClientMain demo = new HttpClientMain();
       
       try
       {
       demo.cmsPingTest();
       }
       catch (Exception e)
       {
       e.printStackTrace();
       }
       }
       ```
       
     
     - 实现接口（包含鉴权）的post请求的主函数如下：
       ```
       public void cmsPingTest() throws Exception
       {
               HttpProxyHelper httpProxy = new HttpProxyHelper();
               //请求接口的URL
               final String httpPath = "/rest/cmsapp/v1/ping";
               final String postUrl = CmsParameters.url + httpPath;
               //构造请求接口中的bodyJson
               JSONObject jsonBody = new JSONObject();
               jsonBody.put("say", "Hello world!");
               String jsonBodyStr = JSONObject.toJSONString(jsonBody);
               // 构造鉴权信息
               Map<String, String> httpHeads = ToolUtils.buildSignHttpHead(httpPath, jsonBodyStr);
               // 执行HTTPS请求，获取返回JSON字符串
               String response = httpProxy.doPost(postUrl, httpHeads, jsonBodyStr);
               //调用成功后，会返回包含responseId的信息
               System.out.println(response);
       }
       ```
       
     
     - 实现接口（包含鉴权）的get请求的主函数如下(对比post请求，get请求的其他代码不变)：
       ```
       public void cmsPingTest() throws Exception
       {
               HttpProxyHelper httpProxy = new HttpProxyHelper();
               //请求接口的URL
               final String httpPath = "/rest/cmsapp/v1/ping";
               final String postUrl = CmsParameters.url + httpPath;
               // GET请求参数
               Map<String, String> queryParameters = new HashMap<>();
               // GET请求构造鉴权信息
               Map<String, String> httpHeads = ToolUtils.buildSignHttpHead(httpPath, null, queryParameters, HttpProxyHelper.HTTP_METHOD_GET);
               System.out.println("httpHeads:" + httpHeads);
               // 执行HTTPS请求，获取返回JSON字符串
               String response = httpProxy.doGet(postUrl, httpHeads, queryParameters);
               //调用成功后，会返回包含responseId的信息
               System.out.println(response);
       }
       ```
       
     
     - 函数ToolUtils.toJsonString()的实现，实际的调用者开发应根据JSON框架直接转换BaseRequest为JSON串，此处仅作为样例：
       ```
       /**
        * 实际客户端开发根据JSON框架直接转换BaseRequest为Json串
        * @param request
        * @return String
        * @throws Exception
        */
       public static String toJsonString(BaseRequest request) throws Exception
       {
       JSONObject jsonObject = new JSONObject();
       StringBuilder buffer = new StringBuilder();
       buffer.append("{");
       buffer.append("\"request\":");
       buffer.append(jsonObject.toJSONString(request.getRequest()));
       buffer.append(",");
       buffer.append("\"msgBody\":");
       buffer.append(jsonObject.toJSONString(request.getMsgBody()));
       buffer.append("}");
       return buffer.toString();
       }
       ```
       
     
     - Https请求POST方法的实现，实际的调用者也可自有选择框架，使用其自带的POST方法：
       ```
       package com.huawei.client.rest.v2.demo;
       import java.io.BufferedInputStream;
       import java.io.ByteArrayOutputStream;
       import java.io.Closeable;
       import java.io.IOException;
       import java.io.InputStream;
       import java.io.OutputStream;
       import java.net.HttpURLConnection;
       import java.net.URL;
       import java.security.KeyManagementException;
       import java.security.NoSuchAlgorithmException;
       import java.security.cert.CertificateException;
       import java.security.cert.X509Certificate;
       import java.util.Map;
       import java.util.Map.Entry;
       import javax.net.ssl.HostnameVerifier;
       import javax.net.ssl.HttpsURLConnection;
       import javax.net.ssl.SSLContext;
       import javax.net.ssl.SSLSession;
       import javax.net.ssl.TrustManager;
       import javax.net.ssl.X509TrustManager;
       public class HttpProxyHelper
       {
       public static final String HTTP_METHOD_POST = "POST";
       public String doPost(String urlAddress, Map<String, String> httpHeads, String bodyJson) throws IOException, NoSuchAlgorithmException, KeyManagementException
       {
       byte[] byteData = bodyJson.getBytes("UTF-8");
       OutputStream out = null;
       InputStream in = null;
       // 建立连接
       this.initHttpsURLConnection();
       URL url = new URL(urlAddress);
       HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
       // 设置参数
       httpConn.setRequestMethod(HttpProxyHelper.HTTP_METHOD_POST);
       httpConn.setRequestProperty("Charset", "UTF-8");
       //httpConn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
       httpConn.setRequestProperty("accept", "application/json");
       //httpConn.setRequestProperty("Content-Length", String.valueOf(byteData.length));
       httpConn.setDoOutput(true);
       httpConn.setDoInput(true);
       httpConn.setUseCaches(false);
       httpConn.setConnectTimeout(20 * 1000);
       httpConn.setReadTimeout(30 * 1000);
       // 设置业务携带参数
       if ((null != httpHeads) && !httpHeads.isEmpty())
       {
       for (Entry<String, String> e : httpHeads.entrySet())
       {
       httpConn.setRequestProperty(e.getKey(), e.getValue());
       }
       }
       try
       {
       // 发送数据
       out = httpConn.getOutputStream();
       out.write(byteData);
       out.flush();
       // 接收数据
       int responseCode = httpConn.getResponseCode();
       if (responseCode != HttpURLConnection.HTTP_OK)
       {
       throw new RuntimeException("Failed responseCode " + responseCode);
       }
       in = httpConn.getInputStream();
       String reponseJson = this.getStreamAsString(in, "UTF-8");
       return reponseJson;
       }
       finally
       {
       this.closeStream(out);
       this.closeStream(in);
       }
       }
       /**
        * 从流获取字符串
        * @param in
        * @param charset
        * @return String
        * @throws IOException
        */
       private String getStreamAsString(InputStream in, String charset) throws IOException
       {
       BufferedInputStream buffer = new BufferedInputStream(in);
       ByteArrayOutputStream out = new ByteArrayOutputStream();
       try
       {
       byte[] cache = new byte[512];
       int count = 0;
       while ((count = buffer.read(cache)) > 0)
       {
       out.write(cache, 0, count);
       }
       }
       finally
       {
       if (buffer != null)
       {
       buffer.close();
       }
       }
       return new String(out.toByteArray(), charset);
       }
       /**
        * 关闭流
        * @param stream
        */
       private void closeStream(Closeable stream)
       {
       if (null != stream)
       {
       try
       {
       stream.close();
       }
       catch (Exception e)
       {
       e.printStackTrace();
       }
       }
       }
       private void initHttpsURLConnection() throws NoSuchAlgorithmException, KeyManagementException
       {
       SSLContext sslcontext = SSLContext.getInstance("SSL");
       HostnameVerifier hnv = new HttpsHostnameVerifier();
       sslcontext.init(null, new TrustManager[] { new HttpsTrustAnyTrustManager() }, new java.security.SecureRandom());
       HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
       HttpsURLConnection.setDefaultHostnameVerifier(hnv);
       }
       private class HttpsTrustAnyTrustManager implements X509TrustManager
       {
       @Override
       public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
       {
       }
       @Override
       public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
       {
       }
       @Override
       public X509Certificate[] getAcceptedIssuers()
       {
       return new X509Certificate[] {};
       }
       }
       private class HttpsHostnameVerifier implements HostnameVerifier
       {
       @Override
       public boolean verify(String hostname, SSLSession session)
       {
       return true;
       }
       }
       }
       ```
       
      
  
  3. 变量参数展示表：
     
     | 变量               | 值                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
     |:---|:---|
     | HttpHead         | Host="10.22.26.181:28080" Content-Length="22" Content-Type="application/json;charset=UTF-8"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
     | accessKey        | globalaktest                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
     | secretKey        | 1qaz\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*20                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
     | HttpURI          | /rest/cmsapp/v1/ping                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
     | HttpMethod       | POST                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
     | timestamp        | 2018-10-17T11:48:24Z                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
     | HttpBody         | {"say": "Hello world!"}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
     | SignedHeaders    | content-length;content-type;host                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
     | CanonicalHeaders | content-length:22\\n content-type:application%2Fjson%3Bcharset%3DUTF-8\\n host:10.22.26.181%3A28080                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
     | CanonicalRequest | POST\\n /rest/cmsapp/v1/ping\\n content-length;content-type;host\\n content-length:22\\n content-type:application%2Fjson%3Bcharset%3DUTF-8\\n host:10.22.26.181%3A28080\\n %7B%22request%22%3A%7B%22version%22%3A%222.0%22%7D%2C%22msgBody%22%3A%7B%22accountId%22%3A%22%22%2C%22beginTime%22%3A%222018-06-29%2010%3A42%3A49%22%2C%22endTime%22%3A%222018-07-02%2010%3A42%3A49%22%2C%22agentId%22%3A%22%22%2C%22callId%22%3A%22%22%2C%22dataType%22%3A%22call_record%22%2C%22callBackURL%22%3A%22http%3A%2F%2F10.57.118.171%3A8080%22%7D%7D |
     | SigningKey       | b25b\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*40                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
     | Signature        | d5a8\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*2f                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
     | Authorization    | auth-v2/globalak/2018-10-17T11:48:24Z/content-length;content-type;host/d5a8\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*2f                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
        
     
   
 
