接口输出能力
A:提供restful-api风格的http接口
B:提供grpc接口
HEAD部分:Content-Type: application/json
POST body部分:{"xxx": "xxxxxxx"}
认证鉴权:调用接口有如下两种认证方式 ,认证鉴权。
Token认证:通过Token认证通用请求。
AuthorizationBearer
· APPkey/AppSecret认证:通过APPkey/AppSecret 签名方式调用请求。适用于应用接入方认证,参照 附录api签名指南部分
AuthorizationZEUS-HMAC-SHA256
认证相关hearder头规范如下:
· Authorization:
目前支持Bearer和自定义ZEUS-HMAC-SHA256
· X-Auth-Subject: 扩展认证主体标识
格式:, 逗号分隔
类型:目前支持两种种类型:
user:用户
app :应用如果是client id为终端ID,如果是用户,ID为staffid,如果是app则是appID
响应规范
响应Content-Type
application/json
即返回结果格式使用 JSON
响应约束
response.header 中的 HTTP status Code 为 2xx 时,表示正常响应;直接返回数据
成功 :
HTTP/1.1 200 OK
Content-Type: application/json; charset="UTF-8"
Content-Length: XXXX
{
"id": 0,
"userName": "john",
"email": "123@",
}
失败 :
HTTP/1.1 500 INTERNAL SERVER ERROR
Content-Type: application/json; charset="UTF-8"
Content-Length: XXXX
{
"code": 3,
"message": "xxx错误",
"details": [],
}
GRPC 接口规范
附录
HTTP常用响应码·
响应 码 | 描述 |
200 | OK – 操作顺利完成 |
201 | Created –- 客户端请求成功创建了一个新资源时,可发送此响应码 |
204 | No Content – 服务器端拒绝对PUT、POST或DELETE请求返回任何状态信息或表示,可发送此响应码 |
301 | Moved Permanently – 客户端触发的动作引起资源URI变化时发送此响应码,或者客户端请求一个资源的旧url时也发送此响 应码 |
400 | Bad Request – 客户端请求参数不合法 |
401 | Unauthorized – 客户端试图操作一个受保护资源,却又没有提供正确的认证证书,可发送此响应码 |
403 | Forbidden – 客户端请求正确,认证也正确,但由于请求资源只允许在指定时间段内,或者只允许指定IP的用户访问,可发送 此响应码 |
404 | Not Found – 客户端请求的资源不存在 |
405 | Method Not Allowed – 客户端试图使用一个本资源不支持的HTTP方法时,可发送此响应码 |
409 | Conflict – 客户端试图执行一个“会导致一个或多个资源处于不一致状态”的操作时,可发送此响应码 |
410 | Gone – 服务器知道被请求资源曾经指向一个资源,但该资源现在不存在了,可发送此响应码 |
413 | Request Entity Too Large – 客户端发送的表示太大以至于服务器无法处理时,可发送此响应码 |
414 | Request-URI Too Long – 客户端请求URL长度超过服务端处理能力时,可发送此响应码 |
415 | Unsupported Media Type – 客户端在发送表示时采用了一个服务端无法理解的媒体类型,可发送此响应码 |
500 | Internal Server Error – 服务器端发生未知故障 |
503 | Service Unavailable – 服务器正常,但由于下层服务不正常(如系统资源不足或正在遭受大量恶意请求)以至于不能全部处 理,可发送此响应码 |
错误码分配表
错误码段 | 模块名称 |
0~9999 | 平台框架使用 参照下面表格中定义的错误码 |
10000 ~ 99999 | 其它模块使用 有业务自己定义 |
…… | …… |
下面列举了状态码code及http 对应关系,该状态码已覆盖大部分场景
错误码 code | 对应http状 态码 | 错误描述 |
0 | 200 | |
1 | 499 | CANCELLED:操作被取消 |
2 | 500 | UNKNOWN-未知的错误。 通常是服务器错误 |
3 | 400 | INVALID_ARGUMENT-客户端指定了无效的参数。 检查错误消息和错误详细信息以获取更多信息。 |
4 | 504 | DEADLINE_EXCEEDED已超过请求期限。如果重复发生,请考虑降低请求的复杂性。 |
5 | 404 | NOT_FOUND找不到指定的资源 |
6 | 409 | ALREADY_EXISTS客户端尝试创建的资源已存在。 |
7 | 403 | PERMISSION_DENIED客户端没有足够的权限。这可能是因为OAuth令牌没有正确的范围,客户端没有权限,或者客 户端项目尚未启用API。 |
8 | 429 | RESOURCE_EXHAUSTED资源配额达到速率限制。如磁盘已满 |
9 | 400 | FAILED_PRECONDITION失败具有前提条件 请求不能在当前系统状态下执行,例如删除非空目录。 |
10 | 409 | ABORTED并发冲突,例如读-修改-写冲突 |
11 | 400 | OUT_OF_RANGE客户端指定了无效的范围。 |
12 | 501 | UNIMPLEMENTED服务器未实现该API方法。 |
13 | 500 | INTERNAL内部服务错误。 通常是服务器错误。 |
14 | 503 | UNAVAILABLE暂停服务。通常是服务器已经关闭。 |
15 | 500 | DATA_LOSS不可恢复的数据丢失或数据损坏。 客户端应该向用户报告错误 |
16 | 401 | UNAUTHENTICATED由于遗失,无效或过期的OAuth令牌而导致请求未通过身份验证。 |
Api签名指南
通过APPKEY/AppSecret签名以及请求发送的流程概述如下:
a. 创建待签名字符串。
b. 使用APPKEY/AppSecret待签字符串计算签名。
c. 将生成的签名信息作为请求消息头添加到HTTP请求HEADER:Authorization中
以下做详细介绍。
步骤1:创建待签名字符串
参数说明
参数 | 必须 | 说明 |
appkey | 是 | 用户中心系统分配 |
timestamp | 是 | 毫秒时间戳 |
nonce | 是 | 一个随机值,建议10位 |
version | 是 | 算法版本号:默认1.0.0 |
待签名字符串:
StringToSign=appKey:{appKey}&nonce:{nonce}×tamp={timestamp}
举例说明:appKey:snc&nonce:1234567890×tamp:1566974140
步骤2:使用APPKey/AppSecret待签字符串计算签名
signature = HMAC256(AppSecret,StringToSign))
假设:StringToSign=
appKey:snc&nonce:1234567890×tamp:1566974140 AppSecret=8095e6-85e6-11e9-bf62-6c92bf0e53 则signature=
步骤3:将生成的签名信息作为请求消息头添加到HTTP请求HEADER:Authorization中
在计算签名后,将它添加到Authorization的HTTP消息头。Authorization消息头未包含在已签名消息头中,主要用于身份验证。
伪代码如下:
Authorization header
Authorization:
为固定值ZEUS-HMAC-SHA256,为 appId nonce timestamp signature 中间是逗号隔开, 和空格隔开
Authorization:ZEUS-HMAC-SHA256 appKey={AppKey},nonce={nonce},timestamp={timestamp},version={version},signature=
{signature}
举例:
Authorization:ZEUS-HMAC-SHA256
appKey=snc,nonce=1234567890,timestamp=1566974140,version=1.0.0,signature=
得到签名消息头后,将其增加到原始HTTP请求内容中
包含签名信息的完整请求如下:
GET /api/v1/staff/123 HTTP/1.1
Host: xxx
Content-Type: application/json
Authorization:ZEUS-HMAC-SHA256 appKey=snc,nonce=1234567890,timestamp=1566974140,version=1.0.0,signature=
X-Auth-Subject:client,xxxxx
认证相关自定义错误码:
CAPTCHA_ERROR errCodeType = 10000 //
LOCK_ACCOUNT_ERR = 10001 //
LOCKED_ACCOUNT_ERR = 10002 //
AUTH_FACTOR_ERR = 10003 //
PASSWORD_EXPIRED_ERR = 10004 //
PASSWORD_EXPIRED_TIME = 10005 //
PASSWORD_POLICIY_ERR = 10006 //
FACTOR_NOT_FOUND_ERR = 10007 //
UNAUTH_ERROR = 10008 //
CAPTCHA_CHECK_ERROR = 10009 //
以创建应用接口为例:
HTTP报文示例:
POST /app/v1/app HTTP/1.1
Host: 10.251.72.200:32165
Accept: application/grpc-web-text
X-User-Agent: grpc-web-javascript/0.1
Authorization: ZEUS-HMAC-SHA256 appKey=b09285a0a7bc4dd3aca3c84dd4f1e927,nonce=4266672601,timestamp=1599552024,
version=1.0.0,signature=72e7febb10177249a1d7fb23aa96329437a5e60c8ff107e7aab94f00def710ec
X-Grpc-Web: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135
Safari/537.36
x-request-id: 8def5010-4d9e-433e-a5aa-55f4d453d51e
Content-Type: application/json
X-Auth-Subject: app,b09285a0a7bc4dd3aca3c84dd4f1e927
{"asset_id":{"oid":2671556498458444238,"id":2671556498458509774},"meta":{"name":"test12663","description":"this is test123","staff_id":{"asset_id":{"oid":2671556498458444238,"id":2671556498458509774},"id":
2672430249123283553}}}
CURL示例:
curl --location --request POST 'https://10.251.72.200:32165/app/v1/app' \
--header 'Accept: application/grpc-web-text' \
--header 'X-User-Agent: grpc-web-javascript/0.1' \
--header 'Authorization: ZEUS-HMAC-SHA256 appKey=b09285a0a7bc4dd3aca3c84dd4f1e927,nonce=4266672601,
timestamp=1599552024,version=1.0.0,signature=72e7febb10177249a1d7fb23aa96329437a5e60c8ff107e7aab94f00def710ec' \
--header 'X-Grpc-Web: 1' \
--header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.
0.4147.135 Safari/537.36' \
--header 'x-request-id: 8def5010-4d9e-433e-a5aa-55f4d453d51e' \
--header 'Content-Type: application/json' \
--header 'X-Auth-Subject: app,b09285a0a7bc4dd3aca3c84dd4f1e927' \
--data-raw '{"asset_id":{"oid":2671556498458444238,"id":2671556498458509774},"meta":{"name":"test12663","
description":"this is test123","staff_id":{"asset_id":{"oid":2671556498458444238,"id":2671556498458509774},"id":
2672430249123283553}}}'
HTTP请求
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://10.251.72.200:32165/app/v1/app"
method := "POST"
payload := strings.NewReader("{\"asset_id\":{\"oid\":2671556498458444238,\"id\":2671556498458509774},\"meta\": {\"name\":\"test12663\",\"description\":\"this is test123\",\"staff_id\":{\"asset_id\":{\"oid\":
2671556498458444238,\"id\":2671556498458509774},\"id\":2672430249123283553}}}")
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
}
req.Header.Add("Accept", "application/grpc-web-text")
req.Header.Add("X-User-Agent", "grpc-web-javascript/0.1")
req.Header.Add("Authorization", "ZEUS-HMAC-SHA256 appKey=b09285a0a7bc4dd3aca3c84dd4f1e927,nonce=4266672601,
timestamp=1599552024,version=1.0.0,signature=72e7febb10177249a1d7fb23aa96329437a5e60c8ff107e7aab94f00def710ec")
req.Header.Add("X-Grpc-Web", "1")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/84.0.4147.135 Safari/537.36")
req.Header.Add("x-request-id", "8def5010-4d9e-433e-a5aa-55f4d453d51e")
req.Header.Add("Content-Type", "application/json")
req.Header.Add("X-Auth-Subject", "app,b09285a0a7bc4dd3aca3c84dd4f1e927")
res, err := client.Do(req)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
fmt.Println(string(body))
}
GRPC示例:
package main
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
api "git-biz.qianxin-inc.cn/zeus-platform/zeus-da-api.git/generated-go/app_v1"
apiClient "git-biz.qianxin-inc.cn/zeus-platform/zeus-da-api.git/generated-go/app_v1/client"
"golang.org/x/net/context"
"google.golang.org/grpc/metadata"
"strconv"
"time"
"git-biz.qianxin-inc.cn/zeus-platform/zeus-da-api.git/generated-go/zeus"
"git-biz.qianxin-inc.cn/zeus-platform/zeus-da-api.git/generated-go/zeus/app"
)
var oid int64 = 2671556498458444238
var assetId int64 = 2671556498458509774
var appId int64 = 2682883730313347430
var staffId int64 = 2672430249123283553
var appKey string = "b09285a0a7bc4dd3aca3c84dd4f1e927"
var appSecret string = "b817187828965ed56d8d72bd90d1ab0c5e2479e3"
const (
VERSION = "1.0.0"
SIGNALG = "ZEUS-HMAC-SHA256"
)
func main() {
GrpcCreateApp()
}
func GrpcCreateApp() {
client, err := apiClient.NewAppV1FatClient()
if err != nil {
fmt.Println(err)
return
}
req := &api.CreateApp_Request{
AssetId: &zeus.AssetId{Oid: oid, Id: assetId},
Meta: &app.Meta{
Name: "test1d0",
Description: "this is test10",
StaffId: &zeus.StaffId{AssetId: &zeus.AssetId{Oid: oid, Id: assetId}, Id: staffId},
},
}
authClient := NewAuthClient(appKey, appSecret)
ctx := authClient.GetCtx()
res, err := client.CreateApp(ctx, req)
fmt.Println(res, err)
return
}
type AuthClient struct {
signAlg string // ZEUS-HMAC-SHA256
appKey string //
appSecret string // appKey
nonce int64 //
timestamp int64 //
version string // 1.0.0
}
func NewAuthClient(appKey, appSecret string) *AuthClient {
random, _ := rand.Prime(rand.Reader, 32)
timestamp := time.Now().Unix()
return &AuthClient{
signAlg: SIGNALG,
appKey: appKey,
appSecret: appSecret,
nonce: random.Int64(),
timestamp: timestamp,
version: VERSION,
}
}
func (c *AuthClient) GetHeader() map[string]string {
return c.assemble()
}
func (c *AuthClient) GetCtx() context.Context {
assemble := c.assemble()
md := metadata.Pairs(
"Authorization", assemble["Authorization"],
"X-Auth-Subject", assemble["x-Auth-Subject"],
)
return metadata.NewOutgoingContext(context.Background(), md)
}
func (c *AuthClient) assemble() map[string]string {
authorization := fmt.Sprintf("%s ", c.signAlg)
authorization += fmt.Sprintf("%s=%s,", "appKey", c.appKey)
authorization += fmt.Sprintf("%s=%s,", "nonce", strconv.FormatInt(c.nonce, 10))
authorization += fmt.Sprintf("%s=%s,", "timestamp", strconv.FormatInt(c.timestamp, 10))
authorization += fmt.Sprintf("%s=%s,", "version", c.version)
authorization += fmt.Sprintf("%s=%s", "signature", c.getSign())
xAuthSubject := fmt.Sprintf("%s,%s", "app", appKey)
return map[string]string{
"Authorization": authorization,
"X-Auth-Subject": xAuthSubject,
}
}
func (c *AuthClient) getSign() string {
signStr := fmt.Sprintf("%s:%s&", "appKey", c.appKey)
signStr += fmt.Sprintf("%s:%s&", "nonce", strconv.FormatInt(c.nonce, 10))
signStr += fmt.Sprintf("%s:%s", "timestamp", strconv.FormatInt(c.timestamp, 10))
return c.getHmac256(signStr)
}
func (c *AuthClient) getHmac256(signStr string) string {
h := hmac.New(sha256.New, []byte(c.appSecret))
h.Write([]byte(signStr))
return hex.EncodeToString(h.Sum(nil))
}