导语
该系列其他篇章:
V3手动鉴权失败之Nodejs篇
V3手动鉴权失败之Go篇
V3手动鉴权失败之Python篇
V3手动鉴权失败之PHP篇
V3手动鉴权失败之C#篇
腾讯云 API 全新升级 3.0 ,该版本进行了性能优化且全地域部署、支持就近和按地域接入、访问时延下降显著,接口描述更加详细、错误码描述更加全面、SDK增加接口级注释,让您更加方便快捷的使用腾讯云产品。人脸识别、文字识别,语音识别等众多产品均已接入云API 3.0。
腾讯云API为了更好的让用户接入,已经封装好了多种语言的SDK,只需用户传入SecrectId、SectectKey以及接口入参,即可完成接口鉴权和请求发送,具体包括Python SDK、Java SDK、PHP SDK、Go SDK、NodeJS SDK、.NET SDK。
案例背景
在某些情况,用户需要实现手动接口鉴权,虽然官网文档已有详细的接口鉴权流程,但是由于:
1.V3手动鉴权步骤较为复杂;
2.官网某些demo代码无法直接下载运行,仍需简单调整;
3.官网文档的demo代码覆盖面有限,没有包括全量上述六类后端语言;
基于此,很多用户只能自己尝试手动鉴权,但都返回“鉴权失败”,从而无法调通接口。
原因分析
从宏观上看,“鉴权失败”要关注两个阶段:
1. 整体的接口鉴权是否正确;
2. 模拟的鉴权请求的发送是否正确;
从历史问题回顾,有客户曾经出现接口鉴权时而成功,时而失败的情况,排查了整体的鉴权过程,完全正确,但是也的确复现了客户的问题。后来发现,用户在鉴权完成后,发送具体的请求时,传入的时间戳timestamp没有实时更新导致了报错。
解决方案
为了帮助客户更简单、更快捷地完成接口手动鉴权,并成功发送鉴权请求,将通过一系列文章专门讲解各个后端语言的手动鉴权&发送请求的可执行demo代码,助力客户快速接入。
本期将以调用人脸识别的DetectFace接口以及文字识别的BusinessCardOCR名片识别接口为例,详叙Java语言
demo。
前期准备
Java语言环境:直接在Java官网根据操作系统类型下载并安装指定SDK安装包即可。
SecrectId和SecretKey:接口鉴权的密钥。可以把SecretId理解成“账号”,把SecretKey理解成“密码”。在自己的腾讯云官网控制台获取:访问管理 -> 访问密钥 -> API密钥管理。
手动鉴权相关文档:请求结构、公共参数、V3接口鉴权
Java代码IDE:笔者使用IDEA,下载并安装,方便使用maven进行包管理。
具体代码
新建一个Maven项目,方便之后的第三方jar包管理:
下面介绍两种方法来实现V3接口鉴权,一种是模拟curl请求,一种是直接发送HTTP请求
method01
在pom.xml文件中添加javax.xml.bind的第三方依赖:
新建名为method01.java的代码文件,具体代码如下(包括了http请求发送和接收响应):
代码语言:javascript复制import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class method01 {
private final static Charset UTF8 = StandardCharsets.UTF_8;
private final static String SECRET_ID = "xxx";//传入自己的secretId
private final static String SECRET_KEY = "xxx";//传入自己的secretKey
public static byte[] hmac256(byte[] key, String msg) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
mac.init(secretKeySpec);
return mac.doFinal(msg.getBytes(UTF8));
}
public static String sha256Hex(String s) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] d = md.digest(s.getBytes(UTF8));
return DatatypeConverter.printHexBinary(d).toLowerCase();
}
public static void main(String[] args) throws Exception {
String service = "iai";
String host = "iai.tencentcloudapi.com";
String region = "ap-guangzhou";
String action = "DetectFace";
String version = "2018-03-01";
String algorithm = "TC3-HMAC-SHA256";
// String timestamp = "1566183698";
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 注意时区,否则容易出错
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = sdf.format(new Date(Long.valueOf(timestamp "000")));
// ************* 步骤 1:拼接规范请求串 *************
String httpRequestMethod = "POST";
String canonicalUri = "/";
String canonicalQueryString = "";
String canonicalHeaders = "content-type:application/jsonn" "host:" host "n";
String signedHeaders = "content-type;host";
// String payload = "{"Limit": 1, "Filters": [{"Values": ["\u672a\u547d\u540d"], "Name": "instance-name"}]}";
String payload = "{"Url":"https://file.digitaling.com/eImg/uimages/20171009/1507508998226085.jpg"}";
// payload = "{"Url":"https://file.digitaling.com/eImg/uimages/20171009/1507508998226085.jpg"}";
System.out.println(payload);
String hashedRequestPayload = sha256Hex(payload);
String canonicalRequest = httpRequestMethod "n" canonicalUri "n" canonicalQueryString "n"
canonicalHeaders "n" signedHeaders "n" hashedRequestPayload;
// System.out.println("CanonicalRequest is: n");
System.out.println(canonicalRequest);
// ************* 步骤 2:拼接待签名字符串 *************
String credentialScope = date "/" service "/" "tc3_request";
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
String stringToSign = algorithm "n" timestamp "n" credentialScope "n" hashedCanonicalRequest;
System.out.println("Timestamp is:" timestamp "n");
System.out.println("Date is: " date "n");
// System.out.println("StingToSign is: n");
System.out.println(stringToSign);
// ************* 步骤 3:计算签名 *************
byte[] secretDate = hmac256(("TC3" SECRET_KEY).getBytes(UTF8), date);
byte[] secretService = hmac256(secretDate, service);
byte[] secretSigning = hmac256(secretService, "tc3_request");
String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
// System.out.println("Signature is:n");
System.out.println(signature);
// ************* 步骤 4:拼接 Authorization *************
String authorization = algorithm " " "Credential=" SECRET_ID "/" credentialScope ", "
"SignedHeaders=" signedHeaders ", " "Signature=" signature;
// System.out.println("Authorization is: n");
System.out.println(authorization);
payload = "{\"Url\":\"https://file.digitaling.com/eImg/uimages/20171009/1507508998226085.jpg\"}";
StringBuilder sb = new StringBuilder();
sb.append("curl -X POST https://").append(host)
.append(" -H "Authorization: ").append(authorization).append(""")
.append(" -H "Content-Type: application/json"")
.append(" -H "Host: ").append(host).append(""")
.append(" -H "X-TC-Action: ").append(action).append(""")
.append(" -H "X-TC-Timestamp: ").append(timestamp).append(""")
.append(" -H "X-TC-Version: ").append(version).append(""")
.append(" -H "X-TC-Region: ").append(region).append(""")
.append(" -d "").append(payload).append(""");
// System.out.println("sb is: n");
System.out.println(sb.toString());
Process process = Runtime.getRuntime().exec(sb.toString());
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
StringBuilder stringBuilder = new StringBuilder();
while ((line = br.readLine()) != null) {
stringBuilder.append(line "n");
}
System.out.println(stringBuilder.toString());
}
}
method02
在pom.xml文件中添加Gson的第三方依赖:
新建名为method02.java的代码文件,具体代码如下(包括了http请求发送和接收响应):
代码语言:javascript复制import com.google.gson.Gson;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
public class method02 {
private static String SecretId = "xxx";//传入自己的secretId
private static String SecretKey = "xxx";//传入自己的secretKey
private static String Url = "https://ocr.tencentcloudapi.com";
//规范请求串
private static String HTTPRequestMethod = "POST";
private static String CanonicalURI = "/";
private static String CanonicalQueryString = "";
private static String CanonicalHeaders = "content-type:application/json; charset=utf-8nhost:ocr.tencentcloudapi.comn";
private static String SignedHeaders = "content-type;host";//参与签名的头部信息
//签名字符串
private static String Algorithm = "TC3-HMAC-SHA256";
private static String Service = "ocr";
private static String Stop = "tc3_request";
//版本
public static String Version = "2018-11-19";
public static String Region = "ap-beijing";
/***
* 示例名片请求方法
* @param args
*/
public static void main(String [] args) {
Map<String, Object> params = new HashMap<>();
params.put("ImageUrl", "https://ocr-demo-1254418846.cos.ap-guangzhou.myzijiebao.com/card/BusinessCardOCR/BusinessCardOCR1.jpg");
Gson gson = new Gson();
String param = gson.toJson(params);
//发送请求 本地封装的https 请求
String response = getAuthTC3("BusinessCardOCR", param, Version);
//打印请求数据
System.out.println(response);
}
/**
* v3鉴权
* @param action 方法名
* @param paramJson json化的参数
* @param version 版本号 2018-03-01
* @return
*/
public static String getAuthTC3(String action, String paramJson, String version){
try{
String hashedRequestPayload = HashEncryption(paramJson);
String CanonicalRequest =
HTTPRequestMethod 'n'
CanonicalURI 'n'
CanonicalQueryString 'n'
CanonicalHeaders 'n'
SignedHeaders 'n'
hashedRequestPayload;
//时间戳
Date date = new Date();
//微秒->秒
String timestamp = String.valueOf(date.getTime() / 1000);
//格林威治时间转化
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
formatter.setTimeZone(TimeZone.getTimeZone("GMT 0"));
String dateString = formatter.format(date.getTime());
//签名字符串
String credentialScope = dateString "/" Service "/" Stop;
String hashedCanonicalRequest = HashEncryption(CanonicalRequest);
String stringToSign = Algorithm "n"
timestamp "n"
credentialScope "n"
hashedCanonicalRequest;
//计算签名
byte[] secretDate = HashHmacSha256Encryption(("TC3" SecretKey).getBytes("UTF-8"), dateString);
byte[] secretService = HashHmacSha256Encryption(secretDate, Service);
byte[] secretSigning = HashHmacSha256Encryption(secretService, Stop);
//签名字符串
byte[] signatureHmacSHA256 = HashHmacSha256Encryption(secretSigning, stringToSign);
StringBuilder builder = new StringBuilder();
for (byte b : signatureHmacSHA256) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' hex;
}
builder.append(hex);
}
String signature = builder.toString().toLowerCase();
//组装签名字符串
String authorization = Algorithm ' '
"Credential=" SecretId '/' credentialScope ", "
"SignedHeaders=" SignedHeaders ", "
"Signature=" signature;
//创建header 头部
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", authorization);
headers.put("Host", "ocr.tencentcloudapi.com");
headers.put("Content-Type", "application/json; charset=utf-8");
headers.put("X-TC-Action", action);
headers.put("X-TC-Version", version);
headers.put("X-TC-Timestamp", timestamp);
headers.put("X-TC-Region", Region);
//request 请求
String response = resquestPostData(Url, paramJson, headers);
return response;
}catch(Exception e){
return e.getMessage();
}
}
/*
* Function : 发送Post请求到服务器
* Param : params请求体内容,encode编码格式
*/
public static String resquestPostData(String strUrlPath, String data, Map<String, String> headers) {
try {
URL url = new URL(strUrlPath);
HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();
httpURLConnection.setConnectTimeout(3000); //设置连接超时时间
httpURLConnection.setDoInput(true); //打开输入流,以便从服务器获取数据
httpURLConnection.setDoOutput(true); //打开输出流,以便向服务器提交数据
httpURLConnection.setRequestMethod("POST"); //设置以Post方式提交数据
httpURLConnection.setUseCaches(false); //使用Post方式不能使用缓存
//设置header
if (headers.isEmpty()) {
//设置请求体的类型是文本类型
httpURLConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
} else {
for (Map.Entry<String, String> entry : headers.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
httpURLConnection.setRequestProperty(key, value);
}
}
//设置请求体的长度
httpURLConnection.setRequestProperty("Content-Length", String.valueOf(data.length()));
//获得输出流,向服务器写入数据
if (data != null) {
byte[] writebytes = data.getBytes();
// 设置文件长度
OutputStream outputStream = httpURLConnection.getOutputStream();
outputStream.write(data.getBytes());
outputStream.flush();
}
int response = httpURLConnection.getResponseCode(); //获得服务器的响应码
if(response == HttpURLConnection.HTTP_OK) {
InputStream inptStream = httpURLConnection.getInputStream();
return dealResponseResult(inptStream); //处理服务器的响应结果
}
} catch (IOException e) {
return "err: " e.getMessage().toString();
}
return "-1";
}
/*
* Function : 封装请求体信息
* Param : params请求体内容,encode编码格式
*/
public static StringBuffer getRequestData(Map<String, String> params, String encode) {
StringBuffer stringBuffer = new StringBuffer(); //存储封装好的请求体信息
try {
for(Map.Entry<String, String> entry : params.entrySet()) {
stringBuffer.append(entry.getKey())
.append("=")
.append(URLEncoder.encode(entry.getValue(), encode))
.append("&");
}
stringBuffer.deleteCharAt(stringBuffer.length() - 1); //删除最后的一个"&"
} catch (Exception e) {
e.printStackTrace();
}
return stringBuffer;
}
/*
* Function : 处理服务器的响应结果(将输入流转化成字符串)
* Param : inputStream服务器的响应输入流
*/
public static String dealResponseResult(InputStream inputStream) {
String resultData = null; //存储处理结果
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] data = new byte[1024];
int len = 0;
try {
while((len = inputStream.read(data)) != -1) {
byteArrayOutputStream.write(data, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
resultData = new String(byteArrayOutputStream.toByteArray());
return resultData;
}
/**
*
*/
private static String HashEncryption(String s) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
sha.update(s.getBytes());
//替换java DatatypeConverter.printHexBinary(d).toLowerCase()
StringBuilder builder = new StringBuilder();
for (byte b : sha.digest()) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' hex;
}
builder.append(hex);
}
return builder.toString().toLowerCase();
}
private static byte[] HashHmacSha256Encryption(byte[] key, String msg) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
mac.init(secretKeySpec);
return mac.doFinal(msg.getBytes("UTF-8"));
}
}