Android 关于腾讯 人脸识别 V3 鉴权代码,供参考
注意:Android9.0 默认是禁止所有的http,且android4.0以后不能在主线程发起网络请求。
代码语言:javascript复制package com.example.iaiface;
import android.text.TextUtils;
import android.util.Log;
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;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* 人脸识别V3鉴权
*/
public class AuthFace {
private static String SecretId = "";
private static String SecretKey = "";
private static String Url = "https://iai.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:iai.tencentcloudapi.comn";
private static String SignedHeaders = "content-type;host";//参与签名的头部信息
//签名字符串
private static String Algorithm = "TC3-HMAC-SHA256";
private static String Service = "iai";
private static String Stop = "tc3_request";
//版本
public static String Version = "2018-03-01";
/**
* v3鉴权
* @param action 人脸识别接口名 例如 DetectFace
* @param paramJson json化的参数
* @param version 版本号 2018-03-01
* @return
*/
public static String getAuthTC3(String action, String paramJson, String version){
try{
String hashedRequestPayload = HashEncryption(paramJson);
Log.e("hashedRequestPayload", hashedRequestPayload);
String CanonicalRequest =
HTTPRequestMethod 'n'
CanonicalURI 'n'
CanonicalQueryString 'n'
CanonicalHeaders 'n'
SignedHeaders 'n'
hashedRequestPayload;
Log.e("CanonicalRequest", CanonicalRequest);
//时间戳
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);
//替换java DatatypeConverter.printHexBinary(d).toLowerCase()
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", "iai.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", "ap-chengdu");
//request 请求
String response = submitPostData(Url, paramJson, headers);
Log.e("response", response);
return response;
}catch(Exception e){
return e.getMessage();
}
}
/**
*
*/
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"));
}
/*
* Function : 发送Post请求到服务器
* Param : params请求体内容,encode编码格式
*/
public static String submitPostData(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");
} 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 && !TextUtils.isEmpty(data)) {
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;
}
}
调用示例:人脸检测与分析
代码语言:javascript复制 Map<String, Object> params = new HashMap<>();
params.put("Url", "https://file.digitaling.com/eImg/uimages/20171009/1507508998226085.jpg");
params.put("FaceModelVersion", "3.0");
Gson gson = new Gson();
String param = gson.toJson(params);
String response = AuthFace.getAuthTC3("DetectFace", param, AuthFace.Version);
1. error: java.net.SocketException: socket failed: EPERM (Operation not permitted)
在常规调试Android HTTP请求或者其他功能(录音、拍照)等,需要在 AndroidManifest.xml 文件中开通权限,
权限集合参考:https://blog.csdn.net/snailbaby_soko/article/details/52954573
例如网络权限开通如下:
代码语言:javascript复制 <uses-permission android:name="android.permission.INTERNET" />
网络状态权限: (此权限不影响使用,可选择不添加)
代码语言:javascript复制<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
重点:仍无法解决,则尝试卸载程序重新安装,导致这原因是:一开始网络请求时没有赋予网络权限,重新给程序赋予权限时没有卸载程序,只是重新编译,系统并没有获取到配置文件中的权限,因此需要卸载重新安装。
2. error : No Network Security Config specified, using platform default
解决方案:
(1)res文件夹下新建xml目录,然后创建network_security_config.xml文件,内容如下
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
(2)之后在application中添加配置如下,即可:
代码语言:javascript复制<application
.....
android:networkSecurityConfig="@xml/network_security_config"
.......>
(3)需要在代码中设置如下代码才可以正常进行网络请求: android:usesCleartextTraffic="true" (本身是Android 6.0以下版本, 但实际也需要添加):指示应用程序是否打算使用明文网络流量
代码语言:javascript复制<application
.....
android:usesCleartextTraffic="true"
.......>
整体示例:
代码语言:javascript复制<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.iaiface">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3. JAVA 中 Hash 时 DatatypeConverter.printHexBinary 在Android 不可使用,替换方案
Java
代码语言:javascript复制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();
}
Android
代码语言:javascript复制private static String sha256Hex(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();
}