本文中含有需要您注意的重要提示信息,忽略該信息可能對您的業務造成影響,請務必仔細閱讀。
本文主要介紹通過HTTPS原生調用方式在服務端集成金融級實人認證的方法。
調用方式
服務接入點:
https://saf.cn-shanghai.aliyuncs.com
傳輸協議:HTTPS
請求方式:POST
QPS限量:API獨享QPS限量,詳情請參見服務端接口QPS限量說明。
發起認證請求
發起認證請求時,傳入通用參數,并在ServiceParameters的JSON字符串里傳入以下字段:
名稱 | 類型 | 是否必選 | 描述 | 示例值 |
method | String | 是 | 發起認證請求的操作。 取值:init。 | init |
sceneId | String | 是 | 認證場景ID,該ID在控制臺創建認證場景后自動生成。關于如何創建認證場景,請參見添加認證場景。 | 100000**** |
outerOrderNo | String | 是 | 客戶服務端自定義的業務唯一標識,用于后續定位排查問題時使用。該值長度為1~32個字符,支持數字和大小寫英文字母,請確保唯一。 | e0c34a77f5ac40a5aa5e6ed20c35**** |
bizCode | String | 是 | 認證場景碼和商戶發起認證的接入端有關:
| FACE_SDK |
identityType | String | 是 | 身份信息的參數類型,必須傳入CERT_INFO。 | CERT_INFO |
certType | String | 是 | 用戶證件類型。支持的證件類型,請參見方案概述。 不同證件類型,取值均為IDENTITY_CARD。 | IDENTITY_CARD |
certNo | String | 是 | 用戶身份證件號碼。 | 1******************9 |
certName | String | 是 | 用戶姓名。 | 張先生 |
returnUrl | String | 是 | 商戶業務頁面回調的目標地址。
| https://www.aliyun.com |
encryptType | String | 否 | 傳入加密算法,目前僅支持SM2國密算法。 開啟加密傳輸后,需傳入加密后的CertName和CertNo。如何加密,請參見參數加密說明。 | SM2 |
發起認證請求后,您會收到返回信息。返回參數列表如下:
名稱 | 類型 | 是否必選 | 描述 | 示例值 |
certifyId | String | 是 | 認證ID,刷臉認證的唯一標識。 警告 CertifyId字段為計費統計字段,為了方便后續核對賬單,請您在本地留存該字段信息。 初始化接口返回的認證CertifyId在30分鐘有效且僅能認證提交一次,請您在有效期內應用,避免重復使用。 | 7eff3ad26a9c7b68c511b9f35eb1**** |
certifyUrl | String | 是 | 認證流程入口Url。 重要 初始化接口返回的認證CertifyUrl在30分鐘有效且僅認證僅能提交一次,請您在有效期內使用,避免重復使用。 | https://picker.antcloudauth.aliyuncs.com/gateway.do?... |
返回示例如下:
{
"code": 200,
"requestId": "5CD69C04-8A6B-4CC3-A0C9-D18D5FEFA788",
"data": {
"certifyUrl": "https://picker.antcloudauth.aliyuncs.com/gateway.do?...",
"certifyId": "7eff3ad26a9c7b68c511b9f35eb1****"
},
"message": "OK"
}
查詢認證結果
查詢認證結果時,傳入通用參數,并在ServiceParameters的JSON字符串里傳入以下字段:
名稱 | 類型 | 是否必選 | 描述 | 示例值 |
method | String | 是 | 查詢認證結果的操作。 取值:query。 | query |
certifyId | String | 是 | 認證ID,需與發起認證請求時返回的certifyId保持一致。 | 7eff3ad26a9c7b68c511b9f35eb1**** |
sceneId | String | 是 | 認證場景ID,需與發起認證請求時的sceneId保持一致。 | 100000***** |
encToken | String | 否 | 公鑰加密后的AES 256密鑰(Base64編碼),用于獲取加密圖片。僅面向持牌金融客戶KYC留存的認證圖片。如何使用RSA公鑰加密AES密鑰,請參見支付寶小程序或H5方案密鑰加密說明。 | l1tmZUQKdclKdGHzy2hljqltxPYPRo42JjRL04JVHxh9wWP19xHZpVvlKy9lxXRXCiWTKfJAaDfw4pKlZfetvZHdVdjroiXaLt7jx68rpp24wCeWo8wgjMF1lBwJmYZGcTrCVJ+Tonn7CHN7ur11Pn9d4IdkbTi5rBoOx8JITTn6krEH6ssZ5Cj3xFJ4/3PyvloU8UX+FQIPpskYR36jHcqs+Nt4EeTK/wWsAQItl6RfI9FN+8UJ42RVNt/IWHuk3U9aQNxzt7Z+7hbvqpBf/uKp4sgP6fbyYhZ+2tM+KXF1ODOYGenQ65wliaS/C1fTqjUJYcONMEW61Te/de4r4A== |
發起查詢請求后,您會收到返回信息。返回參數列表如下:
名稱 | 類型 | 是否必選 | 描述 | 示例值 |
passed | String | 是 | 是否通過認證。取值:
| T |
identityInfo | String | 否 | 預留字段,默認返回為空。 | 無 |
materialInfo | String | 否 | 預留字段,默認返回為空。 | 無 |
通用參數
使用HTTPS方式在服務端調用金融級實人認證時,您必須傳入以下通用參數:
名稱 | 類型 | 是否必選 | 描述 |
Format | String | 是 | 返回值的類型。取值:
|
Version | String | 是 | API版本號,使用日期格式:YYYY-MM-DD。取值:2017-03-31。 |
AccessKeyId | String | 是 | 阿里云頒發給用戶的訪問服務所用的密鑰ID,請參見創建AccessKey。 |
Signature | String | 是 | 簽名結果串,關于簽名的計算方法,請參見接入金融級實人認證服務。 |
SignatureMethod | String | 是 | 簽名方式,取值:HMAC-SHA1。 |
Timestamp | String | 是 | 請求的時間戳。日期格式按照ISO8601標準表示,并需要使用UTC時間。格式:YYYY-MM-DDThh:mm:ssZ。 例如,2022-7-29T12:00:00Z(為北京時間2022年7月29日的20點0分0秒)。 |
SignatureVersion | String | 是 | 簽名算法版本,目前版本是1.0。 |
SignatureNonce | String | 是 | 唯一隨機數,用于防止網絡重放攻擊。用戶在不同請求間要使用不同的隨機數值。 |
Action | String | 是 | 取值必須是ExecuteRequest。 |
Service | String | 是 | 具體服務名,本服務該參數固定為fin_face_verify。 |
ServiceParameters | String | 是 | 具體服務參數的JSON格式串。具體定義見上文各接口參數定義。 |
通過調用不同服務實現不同功能時,實際是通過傳入對應服務的ServiceParameters來實現的。
調用服務后,您會收到返回碼。以下是返回碼列表:
Code | Message |
200 | 成功。 |
401 | 參數非法。 |
402 | 應用配置不存在。 |
403 | 主賬號*******,無權調用fin_face_verify服務,或服務已到期、已到最大流量、未購買服務。 |
404 | 認證場景配置不存在。 |
406 | 無效的certifyId。 |
407 | 認證已失效。 |
408 | 開放認證單據已失效。 |
501 | 系統錯誤。 |
502 | 系統繁忙。 |
503 | 系統錯誤。 |
完整代碼示例(Java語言)
添加POM依賴:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>credentials-java</artifactId>
<version>0.2.11</version>
</dependency>
調用接口發起認證和查詢認證結果:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SimpleTimeZone;
import java.util.UUID;
import com.google.common.io.BaseEncoding;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
public class SafTest {
public static void main(String[] args) throws Throwable {
// 阿里云賬號AccessKey擁有所有API的訪問權限,建議您使用RAM用戶進行API訪問或日常運維。
// 強烈建議不要把AccessKey ID和AccessKey Secret保存到工程代碼里,否則可能導致AccessKey泄露,威脅您賬號下所有資源的安全。
// 本示例通過阿里云Credentials工具從環境變量中讀取AccessKey,來實現API訪問的身份驗證。如何配置環境變量,請參見http://bestwisewords.com/document_detail/378657.html。
com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client();
String appKey = credentialClient.getAccessKeyId();
String appSecret = credentialClient.getAccessKeySecret();
//參數。
Map<String, String> serviceParameters = new HashMap<String, String>();
//發起認證請求。
serviceParameters.put("method", "init");
serviceParameters.put("sceneId", "100000****");
serviceParameters.put("outerOrderNo", "e0c34a77f5ac40a5aa5e6ed20c35****");
serviceParameters.put("bizCode", "FACE");
serviceParameters.put("identityType", "CERT_INFO");
serviceParameters.put("certType", "IDENTITY_CARD");
serviceParameters.put("certNo", "1******************9");
serviceParameters.put("certName", "張先生");
serviceParameters.put("returnUrl", "https://www.aliyun.com");
/*
// 如需開啟個人信息加密傳輸。
serviceParameters.put("encryptType", "SM2");
serviceParameters.put("certNo", "BMjsstxK3S4b1YH*****Pet8ECObfxmLN92SLsNg==");
serviceParameters.put("certName", "BCRD/7ZkNy7Q*****M1BMBezZe8GaYHrLwyJv558w==");
*/
/*
查詢認證結果。
serviceParameters.put("method", "query");
serviceParameters.put("certifyId", "7eff3ad26a9c7b68c511b9f35eb1****");
serviceParameters.put("sceneId", "100000****");
*/
Map<String, String> params = new HashMap<>();
// 接口業務參數-固定為fin_face_verify。
params.put("Service", "fin_face_verify");
// 附加上業務詳細參數,服務詳細參數,具體見文檔里的業務參數部分,業務參數整體轉換為JSON格式。
params.put("ServiceParameters", com.alibaba.fastjson.JSON.toJSONString(serviceParameters));
doPOPRequest(appKey, appSecret, safUrl, "ExecuteRequest", "2017-03-31", params);
}
public static String doPOPRequest(String appKey, String appSecret, String url, String action, String version, Map<String, String> parameters) throws Throwable {
String encoding = "UTF-8";
Map<String, String> paramsForPOP = new HashMap<>();
// 公共參數-固定。
paramsForPOP.put("AccessKeyId", appKey);
paramsForPOP.put("Format", "JSON");// 固定。
paramsForPOP.put("SignatureMethod", "HMAC-SHA1");// 固定。
paramsForPOP.put("Timestamp", formatIso8601Date(new Date()));// 注意格式是:YYYY-MM-DDThh:mm:ssZ。
paramsForPOP.put("SignatureVersion", "1.0");// 固定。
paramsForPOP.put("SignatureNonce", UUID.randomUUID().toString());// 自行生成一個隨機串,每次請求不可重復。
paramsForPOP.put("Version", version);// 固定。
paramsForPOP.put("Action", action);
if (parameters != null) {
parameters.remove("Signature");
paramsForPOP.putAll(parameters);
}
String signature = computeSignature(paramsForPOP, appSecret, encoding);
paramsForPOP.put("Signature", signature);// 把簽名附加到post參數里。
String result = httpPost(url, paramsForPOP, encoding);
// 返回值。
System.out.println(result);
return result;
}
private static String httpPost(String url, Map<String, String> paramsForPOP, String encoding) throws IOException {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
for (Entry<String, String> entry: paramsForPOP.entrySet()) {
urlParameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
httpPost.setEntity(new UrlEncodedFormEntity(urlParameters, Consts.UTF_8));
CloseableHttpResponse response = httpClient.execute(httpPost);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
return result.toString();
}
private static String formatIso8601Date(Date date) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
df.setTimeZone(new SimpleTimeZone(0, "GMT"));// 注意使用GMT時間。
return df.format(date);
}
public static String computeSignature(Map<String, String> parameters, String secret, String encoding)
throws Exception {
// 將參數Key按字典順序排序。
String[] sortedKeys = parameters.keySet().toArray(new String[] {});
Arrays.sort(sortedKeys);
String separator = "&";
boolean first = true;
// 生成規范化請求字符串。
StringBuilder sb = new StringBuilder();
for (String key : sortedKeys) {
if (first) {
first = false;
} else {
sb.append("&");
}
sb.append(encode(key, encoding)).append("=").append(encode(parameters.get(key), encoding));
}
// 生成用于計算簽名的字符串toSign。
StringBuilder toSign = new StringBuilder();
toSign.append("POST").append(separator);
toSign.append(encode("/", encoding)).append(separator);
toSign.append(encode(sb.toString(), encoding));
return getSignature("HmacSHA1", (secret + separator).getBytes(encoding), toSign.toString().getBytes(encoding));
}
private static String encode(String value, String encoding) throws UnsupportedEncodingException {
if (value == null) { return null; }
// 使用URLEncoder.encode編碼后,將"+","*","%7E"做替換即滿足 API規定的編碼規范。
return URLEncoder.encode(value, encoding).replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
}
public static String getSignature(String algorithm, byte[] secret, byte[] data) {
try {
javax.crypto.Mac mac = javax.crypto.Mac.getInstance(algorithm);
mac.init(new javax.crypto.spec.SecretKeySpec(secret, algorithm));
return BaseEncoding.base64().encode(mac.doFinal(data));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}