知识库类接口鉴权方式
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获取方式
- 集成环境:请联系运营人员获取。
接口鉴权如果通过 ,则http响应码为200;如果不通过,则http的响应码为401。
鉴权算法
下面以CC-CMS接口为例介绍鉴权算法,其余容器化网元CC-iKBS、虚拟化网元CC-FS、CC-iSales的鉴权算法可直接参考。
所有接口服务器端都会对客户端的请求数据做鉴权签名验证,鉴权流程如下:

调用者生成Authorization的生成规则内容如下:

各内容的算法和规则
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'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
调用者对HTTP请求中的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
- 把Header name都改为小写,即调用lowerCase()函数;
- 把上面Header name转换后的字符后面追加分割符";",生成一条记录,注意最后一个字段不追加";";
- 把上面所有记录按照字典排序,然后按照顺序连接成一个大字符串。
- 代码实现:
- 构建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));以上函数的完整代码参见:1.如何调通第一个接口:
- 函数: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; }
- 构建signedHeaders的Map,填充所需字段信息,然后调用lowerCaseSignedHeaders函数进行字符处理:
- 具体例子:
例子:参与编码的HttpHead如下: Host="10.22.26.181:28080" Content-Length="22" Content-Type="application/json;charset=UTF-8" 经过上述规则处理后为: SignedHeaders=content-length;content-type;host
- 内容计算规则:
- CanonicalHeaders
其要求计算的编码规则与SignedHeaders一致,但增加了head value的编码。
- 内容计算规则:
遍历参与编码的HttpHead里面的head name
- 把Header name都改为小写,即调用lowerCase()函数;
- 调用NormalizePath函数,对刚才转换后的小写字符串进行格式化;
- 格式化后的字符串+":"+NormalizePath((Header value).trim()),生成一条记录字符串;
- 把上面的记录按照字典排序,进行排序;
- 遍历排序后的记录,中间追加字符串"\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)
参数描述:
- $HttpMethod,指HTTP协议中定义的GET、PUT、POST等请求,必须使用全大写的形式。所涉及的HTTP Method有:GET、POST、PUT、DELETE、HEAD,但当前CC-CMS服务仅支持POST这1种;
- $HttpURI,指接口请求的http URI,比如完整url为https://10.22.26.181:28080/rest/cmsapp/v1/ping那么HttpURI就为/rest/cmsapp/v1/ping ,必须以“/”开头,不以“/”开头的需要补充上,空路径为“/”;
- $HttpParameters, 指接口请求URI后面的请求参数,比如https://10.22.26.181:28080/rest/cmsapp/v1/ping?id=123&name=test,那么HttpParameters就为id=123&name=test,CC-CMS接口目前没有该参数。
- $HttpBody:是通过HTTP BODY体提交到服务器端的字符串,该字符串的形式为标准JSON串,具体字段见各接口的定义。
- 代码实现:
参见•SignInfo中的canonicalRequest()函数。
- 内容计算规则:
- 生成认证字符串Authorization
- 内容计算规则:
- 首先生成authStringPrefix和SigningKey
- 关于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'Z'";
- 代码实现:authStringPrefix
生成authStringPrefix的函数在SignInfo类的如下两个函数中实现
authStringPrefix() appendSignedHeaders(StringBuilder buffer)
- 代码实现:SigningKey:
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; } }
- 关于authStringPrefix和SigningKey的生成规则:
- 生成签名Signature
- 签名的生成规则:
Signature = sha256Hex(SigningKey, CanonicalRequest)
- 代码实现:包含加密函数:sha256Hex
参见上述:c.代码实现:SigningKey:SigningKey中的sha256Hex()实现方法。
- 签名的生成规则:
- 认证字符串
- 认证字符串的生成规则:
Authorization:$authStringPrefix/$Signature
- 代码实现:其中,signedHeaders为上述signedHeaders模块中构建的Map。
// 生成签名 String signature = signInfo.authString(); Map<String, String> httpHeads = new HashMap<>(signedHeaders); // 追加到HTTPHEAD,发送到服务器端 httpHeads.put(SignInfo.HEAD_AUTHORIZATION, signature);
- 认证字符串的生成规则:
- 首先生成authStringPrefix和SigningKey
- 内容计算规则:
- 完整例子
- 如何调通第一个接口:
调通一个接口,鉴权是必要的一环。
在该项目中,鉴权入口在于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; } } - 代码实现:
- 建立工程的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); // 执行HTTP请求,获取返回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); // 执行HTTP请求,获取返回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(); } - Http请求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; } } }
- 建立工程的main函数:
- 变量参数展示表:
变量
值
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
- 如何调通第一个接口: