请求地址
http请求地址:
视具体的ai能力而定
http://api.xf-yun.com/v1/private/Service_ID
websocket请求地址:
视具体的ai能力而定
wss://api.xf-yun.com/v1/private/Service_ID
其中Service_ID为创建AI能力生产的能力ID
握手&鉴权
websocket和http协议均需要进行签名,websocket在握手阶段,请求方需要对请求进行签名,服务端通过签名来校验请求的合法性,其中websocket协议为GET请求,http协议为POST请求。以下示例均为websocket协议,如能力为http协议,请更换为POST请求。
鉴权方法:通过在请求地址后面加上鉴权相关参数的方式。 ws示例url:
wss://api.xf-yun.com/v1/private/Service_ID?authorization=YXBpX2tleT0ia2V5eHh4eHh4eHg4ZWUyNzkzNDg1MTlleHh4eHh4eHgiLCBhbGdvcml0aG09ImhtYWMtc2hhMjU2IiwgaGVhZGVycz0iaG9zdCBkYXRlIHJlcXVlc3QtbGluZSIsIHNpZ25hdHVyZT0iSHAzVHk0WmtTQm1MOGpLeU9McFFpdjlTcjVudm1lWUVIN1dzTC9aTzJKZz0i&date=Wed%2C%2010%20Jul%202019%2007%3A35%3A43%20GMT&host=api.xf-yun.com
http示例url:
https://api.xf-yun.com/v1/private/Service_ID?authorization=YXBpX2tleT0ia2V5eHh4eHh4eHg4ZWUyNzkzNDg1MTlleHh4eHh4eHgiLCBhbGdvcml0aG09ImhtYWMtc2hhMjU2IiwgaGVhZGVycz0iaG9zdCBkYXRlIHJlcXVlc3QtbGluZSIsIHNpZ25hdHVyZT0iNFZza0lKSDNVUkM0L2ZwYlgvRnJ1bU9ISHVCU2svZUdsVXYrUmtmeUcxOD0i&host=api.xf-yun.com&date=Tue%2C+22+Dec+2020+06%3A22%3A46+GMT
鉴权参数
参数 | 类型 | 必须 | 说明 | 示例 |
---|---|---|---|---|
host | string | 是 | 请求主机 | api.xf-yun.com |
date | string | 是 | 当前时间戳,RFC1123格式(Wed, 10 Jul 2019 07:35:43 GMT) | Wed, 10 Jul 2019 07:35:43 GMT |
authorization | string | 是 | 使用base64编码的签名相关信息(签名基于hmac-sha256计算) | 参考下方authorization参数生成规则 |
· date参数生成规则
date必须是UTC+0或GMT时区,RFC1123格式(Wed, 10 Jul 2019 07:35:43 GMT)。 服务端会对Date进行时钟偏移检查,最大允许300秒的偏差,超出偏差的请求都将被拒绝。
authorization参数详细生成规则
在AICloud控制台,创建平台应用后即可获取接口密钥APIKey 和 APISecret ,快速指引
参数authorization base64编码前(authorization_origin)的格式如下
api_key="$api_key",algorithm="hmac-sha256",headers="host date request-line",signature="$signature"
api_key 是向平台申请的api_key, algorithm 是加密算法,headers 是参与签名的参数。signature 是使用加密算法对参与签名的参数签名后并使用base64编码的字符串
- signature的生成规则:
signature原始字段由 host,date,request-line三个参数按照格式拼接成 拼接的格式为(\n为换行符,':'后面有一个空格):
host: $host\ndate: $date\n$request-line
例如:
请求的url为 wss://api.xf-yun.com/v1/private/Service_ID
date = Wed, 10 Jul 2019 07:35:43 GMT
那么 signature原始字段则为: signature_origin:
host: api.xf-yun.com
date: Wed, 10 Jul 2019 07:35:43 GMT
GET /v1/private/Service_ID HTTP/1.1
- 使用hmac-sha256算法结合apiSecret对signature_origin签名,获得签名后的摘要signature_sha
signature_sha=hmac-sha256(signature_origin,$apiSecret)
其中 apiSecret 是在控制台获取的APISecret
- 使用base64编码对signature_sha进行编码获得最终的signature
signature=base64(signature_sha)
这样就生成了signature参数(注意:signature_sha是未经过任何编码的原始字节)
假设
APISecret = secretxxxxxxxx2df7900c09xxxxxxxx
date = Wed, 10 Jul 2019 07:35:43 GMT
则
signature=4VskIJH3URC4/fpbX/FrumOHHuBSk/eGlUv+RkfyG18=
那么生成的base64编码前authorization_origin参数为:
api_key="keyxxxxxxxx8ee279348519exxxxxxxx", algorithm="hmac-sha256", headers="host date request-line", signature="4VskIJH3URC4/fpbX/FrumOHHuBSk/eGlUv+RkfyG18="
注: headers是参与签名的参数,请注意是固定的参数名("host date request-line"),而非这些参数的值。
- 最后再对authorization_origin进行base64编码获得最终的authorization参数。
authorization = base64(authorization_origin)
示例:
authorization=YXBpX2tleT0ia2V5eHh4eHh4eHg4ZWUyNzkzNDg1MTlleHh4eHh4eHgiLCBhbGdvcml0aG09ImhtYWMtc2hhMjU2IiwgaGVhZGVycz0iaG9zdCBkYXRlIHJlcXVlc3QtbGluZSIsIHNpZ25hdHVyZT0iNFZza0lKSDNVUkM0L2ZwYlgvRnJ1bU9ISHVCU2svZUdsVXYrUmtmeUcxOD0i
如果握手成功,会返回HTTP 101状态码,表示协议升级成功;如果握手失败,则根据不同错误类型返回不同HTTP Code状态码,同时携带错误描述信息,详细错误说明如下:
HTTP Code | 说明 | 错误描述信息 | 解决方式 |
---|---|---|---|
401 | 缺少authorization请求参数 | {"message":"Unauthorized"} | 按照上面的步骤构建authorization参数,见authorization参数详细生成规则 |
403 | 时钟偏移校验失败 | {"message":"HMAC signature cannot be verified, a valid date or x-date header is required for HMAC Authentication"} | date参数时间戳格式不对或者时间已经过期,与服务端时间戳偏差超过了300s,请检查传入的date参数 |
401 | 签名参数解析失败 | {"message":"HMAC signature cannot be verified"} | authorization参数解析失败,检查authorization参数是否符合 协议要求 |
401 | 签名校验失败 | {"message":"HMAC signature does not match"} | 签名验证失败,可能原因有很多。1. 检查api_key,api_secret 是否正确 2.检查计算签名的参数host,date,request-line是否按照协议要求拼接。 3. 检查signature签名的base64长度是否正常,正常44个字节,如果超出此范围,则可能是参与base64计算的签名信息已经经过编码了,需要使用原始字节进行base64编码。 |
握手失败返回示例:
HTTP/1.1 401 Forbidden
Date: Thu, 06 Dec 2018 07:55:16 GMT
Content-Length: 116
Content-Type: text/plain; charset=utf-8
{
"message": "HMAC signature does not match"
}
生成鉴权url示例代码
golang
//@hosturl : like wss://api.xf-yun.com/v1/private/Service_ID
//@apikey : apiKey
//@apiSecret : apiSecret
func assembleAuthUrl(hosturl string, apiKey, apiSecret string) string {
ul, err := url.Parse(hosturl)
if err != nil {
fmt.Println(err)
}
//签名时间
date := time.Now().UTC().Format(time.RFC1123)
//参与签名的字段 host ,date, request-line
signString := []string{"host: " + ul.Host, "date: " + date, "GET " + ul.Path + " HTTP/1.1"}
//拼接签名字符串
sgin := strings.Join(signString, "\n")
//签名结果
sha := HmacWithShaTobase64("hmac-sha256", sgin, apiSecret)
//构建请求参数 此时不需要urlencoding
authUrl := fmt.Sprintf("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey,
"hmac-sha256", "host date request-line", sha)
//将请求参数使用base64编码
authorization:= base64.StdEncoding.EncodeToString([]byte(authUrl))
v := url.Values{}
v.Add("host", ul.Host)
v.Add("date", date)
v.Add("authorization", authorization)
//将编码后的字符串url encode后添加到url后面
callurl := hosturl + "?" + v.Encode()
return callurl
}
func HmacWithShaTobase64(algorithm, data, key string) string {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(data))
encodeData := mac.Sum(nil)
return base64.StdEncoding.EncodeToString(encodeData)
}
java:
package com.iflytek.webgatews.wsclient;
import okhttp3.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 鉴权使用
* @Date:2019/7/31 15:23
*/
public class AuthUtils {
/**
* 生成用于鉴权的URL,websocket 接口
* @param requestUrl
* @param apiKey
* @param apiSecret
* @return final requestUrl
*/
public static String assembleRequestUrl(String requestUrl, String apiKey, String apiSecret) {
URL url = null;
String httpRequestUrl = requestUrl.replace("ws://", "http://").replace("wss://","https://" );
try {
url = new URL(httpRequestUrl);
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = format.format(new Date());
// date = "Thu, 19 Dec 2024 07:47:57 GMT";
String host = url.getHost();
StringBuilder builder = new StringBuilder("host: ").append(host).append("\n").//
append("date: ").append(date).append("\n").//
append("GET ").append(url.getPath()).append(" HTTP/1.1");
Charset charset = Charset.forName("UTF-8");
Mac mac = Mac.getInstance("hmacsha256");
System.out.println(builder.toString());
SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
mac.init(spec);
byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
String sha = Base64.getEncoder().encodeToString(hexDigits);
String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
String authBase = Base64.getEncoder().encodeToString(authorization.getBytes(charset));
return String.format("%s?authorization=%s&host=%s&date=%s", requestUrl, URLEncoder.encode(authBase), URLEncoder.encode(host), URLEncoder.encode(date));
} catch (Exception e) {
throw new RuntimeException("assemble requestUrl error:"+e.getMessage());
}
}
}
javascript:
function assembleRequestUrl(host,path,apiKey,apiSecret) {
var url = "wss://"+host+path
var date = new Date().toGMTString()
var algorithm = 'hmac-sha256'
var headers = 'host date request-line'
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET ${path} HTTP/1.1`
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
var signature = CryptoJS.enc.Base64.stringify(signatureSha)
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
var authorization = btoa(authorizationOrigin)
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
return url
}
*python
from datetime import datetime
from wsgiref.handlers import format_date_time
from time import mktime
import hashlib
import base64
import hmac
from urllib.parse import urlencode
import os
import traceback
import json
class AssembleHeaderException(Exception):
def __init__(self, msg):
self.message = msg
class Url:
def __init__(this, host, path, schema):
this.host = host
this.path = path
this.schema = schema
pass
# calculate sha256 and encode to base64
def sha256base64(data):
sha256 = hashlib.sha256()
sha256.update(data)
digest = base64.b64encode(sha256.digest()).decode(encoding='utf-8')
return digest
def parse_url(requset_url):
stidx = requset_url.index("://")
host = requset_url[stidx + 3:]
schema = requset_url[:stidx + 3]
edidx = host.index("/")
if edidx <= 0:
raise AssembleHeaderException("invalid request url:" + requset_url)
path = host[edidx:]
host = host[:edidx]
u = Url(host, path, schema)
return u
# build websocket auth request url
def assemble_ws_auth_url(requset_url, method="GET", api_key="", api_secret=""):
u = parse_url(requset_url)
host = u.host
path = u.path
now = datetime.now()
date = format_date_time(mktime(now.timetuple()))
print(date)
# date = "Thu, 12 Dec 2019 01:57:27 GMT"
signature_origin = "host: {}\ndate: {}\n{} {} HTTP/1.1".format(host, date, method, path)
# print(signature_origin)
signature_sha = hmac.new(api_secret.encode('utf-8'), signature_origin.encode('utf-8'),
digestmod=hashlib.sha256).digest()
signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
api_key, "hmac-sha256", "host date request-line", signature_sha)
authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
# print(authorization_origin)
values = {
"host": host,
"date": date,
"authorization": authorization
}
return requset_url + "?" + urlencode(values)