调用方式

接口基本信息:
  • 服务请求地址:https://saf.cn-shanghai.aliyuncs.com
  • 协议:HTTPS
  • 方式:POST

发起认证请求

发起认证请求时,传入通用参数,并在ServiceParameters的 json 字符串里传入以下字段:

名称 类型 是否必需 描述 示例值
method String 发起认证请求的操作。

取值必须是init

init
sceneId String 认证场景ID,在控制台创建认证场景后自动生成。 1000000006
outerOrderNo String 商户请求的唯一标识,值为32位长度的字母数字组合前面几位字符是商户自定义的简称,中间可以使用一段时间,后段可以使用一个随机或递增序列。 e0c34a77f5ac40a5aa5e6ed20c353888
bizCode String 认证场景码和用户发起认证的端有关:
  • 当用户在 iOS 或安卓平台发起认证时,认证场景码是 FACE_SDK
  • 当用户在小程序中或 H5 页面中发起认证时,认证场景码是 FACE
FACE_SDK
identityType String 身份信息参数类型,必须传入 CERT_INFO。 CERT_INFO
certType String 证件类型,当前支持身份证,必须传入IDENTITY_CARD。 IDENTITY_CARD
certNo String 用户身份证件号。 330103xxxxxxxxxxxx
certName String 用户姓名。 张三
returnUrl String 商户业务页面回调的目标地址。
  • 如您不需要回调商户业务页面,您可以在此处传入空字符串。
  • 当您采用端外唤起支付宝认证页面接入,且希望您的用户唤起支付宝完成认证后,能够跳回您的应用页面,您需要在此参数下传入您的应用的 Scheme。详情请参看:回跳原应用
https://www.aliyun.com

发起认证后,您会收到响应。相应参数列表如下:

名称 类型 是否必需 描述 示例值
certifyId String 认证ID,刷脸认证唯一标识。 7eff3ad26a9c7b68c511b9f35eb1a354
certifyUrl String 认证流程入口 URL。 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": "7eff3ad26a9c7b68c511b9f35eb1a354"
    },
    "message": "OK"
}

查询认证结果

查询认证结果时,传入通用参数,并在ServiceParameters的 json 字符串里传入以下字段:

名称 类型 是否必需 描述 示例值
method String 查询人脸比对结果的操作。

取值必须为query

query
certifyId String 认证ID,需与发起认证请求时返回的certifyId保持一致。 7eff3ad26a9c7b68c511b9f35eb1a354
sceneId String 认证场景ID,需与发起认证请求时的sceneId保持一致。 1000000006

发起查询后,您会收到响应。相应参数列表如下:

名称 类型 是否必需 描述 示例值
passed String 是否通过,通过为T,不通过为F。 T
identityInfo String 认证的主体信息,一般的认证场景返回为空。 "IdentityInfo": "{"cert_type":"IDENTITY_CARD","cert_no":"330103xxxxxxxxxxxx","cert_name":"张三"}"
materialInfo String 认证主体附件信息,主要为图片类材料,一般的认证场景都是返回空。 {"facial_picture_front":{ "FEATURE_FACE":"xydasf=="}}

通用参数

在使用 HTTPS 方式在服务端调用金融级实人认证时,您必须传入以下通用参数:
名称 类型 是否必需 描述
Format String 返回值的类型,支持JSONXML,默认为XML
Version String API版本号,为日期形式:YYYY-MM-DD,本版本对应为2017-03-31
AccessKeyId String 阿里云颁发给用户的访问服务所用的密钥ID,请参见 创建AccessKey
Signature String 签名结果串,关于签名的计算方法,请参见 API签名机制
SignatureMethod String 签名方式,目前支持HMAC-SHA1
Timestamp String 请求的时间戳。日期格式按照ISO8601标准表示,并需要使用UTC时间。格式为:YYYY-MM-DDThh:mm:ssZ。

例如,2014-7-29T12:00:00Z(为北京时间2014年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 OK
401 参数非法。
402 应用配置不存在。
403 主账号*******,无权调用 fin_face_verify 服务,或已到期、到量、未购买。
404 认证场景配置不存在
405 以下两种原因之一:
  • 身份认证记录不存在;
  • 您的年龄未满14周岁,不允许使用此产品。
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>
调用接口发起认证和查询认证结果:
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 {
        //阿里云账号的 appKey 和 appSecret
        String appKey = "<accessKey>";
        String appSecret = "<accessSecret>";
        String safUrl = "https://saf.cn-shanghai.aliyuncs.com";

        // 参数
        Map<String, String> serviceParameters = new HashMap<String, String>();
        // 发起认证请求
        serviceParams.put("method",  "init");
        serviceParams.put("sceneId",  "1000000006");
        serviceParams.put("outerOrderNo",  "e0c34a77f5ac40a5aa5e6ed20c353888");
        serviceParams.put("bizCode", "FACE");
        serviceParams.put("identityType", "CERT_INFO");
        serviceParams.put("certType", "IDENTITY_CARD");
        serviceParams.put("certNo", "330103xxxxxxxxxxxx");
        serviceParams.put("certName", "张三");
        serviceParams.put("returnUrl", "https://www.aliyun.com");

        /*
        // 查询认证结果
        serviceParams.put("method",  "query");
        serviceParams.put("certifyId", "7eff3ad26a9c7b68c511b9f35eb1a354");
        serviceParams.put("sceneId", "1000000006");
        */

        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;
    }
}