跳到主要内容

开放接口 SDK 使用说明

Java

pom依赖

<dependency>
<groupId>com.alibaba.platform.shared</groupId>
<artifactId>xxpt.gateway.shared.client</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>

xxpt依赖包可以下载和使用下面的压缩包:

📎xxpt.gateway.shared.client.zip

代码使用样例

POST模式发起客户端请求

String api = "/rpc/enhancedUserQuery/findUsers/byEmpIds.json";
PostClient postClient = ExecutableClient.getInstance().newPostClient(api);
//加第一个工号
postClient.addParameter("empIds", "xxxxx");
//加第二个工号
postClient.addParameter("empIds", "yyyyy");
String apiResult = postClient.post();

GET 模式发起客户端请求

String api = "/rpc/enhancedUserQuery/findUsers/byEmpIds.json";
GetClient getClient = ExecutableClient.getInstance().newGetClient(api);
//加第一个工号
getClient.addParameter("empIds", "xxxxx");
//加第二个工号
getClient.addParameter("empIds", "yyyyy");
String apiResult = getClient.get();
  1. 如果api的某些参数支持多值【数组】场景时可以多次调用addParameter方法,内部会实现追加然后组成数组

Python

SDK依赖


目前python项目调用buc的api服务,可以参照下面的api例子调用服务。

请注意:如果涉及多个参数,请对参数按照升序进行排序。

代码示例


#!/usr/bin/env python
# coding=UTF-8
import urlparse
import urllib
import urllib2
import datetime
import time
import random
import base64
import hmac
import hashlib
import uuid
import socket

class ExecutableClient(object):
#API_KEY:你的应用的access_key
API_KEY = ''
#你的应用的secret_key
API_SECRET_KEY = ''
#API版本,默认1.0
API_VERSION = '1.0'
API_SERVER = ''
#API name
API_NAME = ''
API_URL = API_SERVER + API_NAME

def __init__(self):
self.ipaddress = '127.0.0.1'#self.get_ip_address()
self.method = 'GET'
self.params = {}

@classmethod
def get_mac_address(cls):
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])

@classmethod
def get_ip_address(cls):
# 获取本机电脑名
my_name = socket.getfqdn(socket.gethostname())
# 获取本机ip
my_addr = socket.gethostbyname(my_name)
return my_addr

def get_signature(self, timestamp, nonce, url_info):
bytes_to_sign = '{method}\n{timestamp}\n{nonce}\n{uri}\n{params}'.format(
method=self.method,
timestamp=timestamp,
nonce=nonce,
uri=url_info.path,
params=urllib.unquote(url_info.query)
)
dig = hmac.new(self.API_SECRET_KEY, bytes_to_sign, digestmod=hashlib.sha256).digest()
return base64.b64encode(dig)

def set_ip_addr(self, ip):
self.ipaddress = ip

def set_access_key(self, api_key):
self.API_KEY = api_key

def set_secret_key(self, api_secret_key):
self.API_SECRET_KEY = api_secret_key

def set_domain_name(self, domain_name):
self.API_SERVER = domain_name

def set_api_name(self, api_name):
self.API_NAME = api_name
self.API_URL = self.API_SERVER + self.API_NAME

def set_api_version(self,api_version):
self.API_VERSION = api_version

def add_params(self, field, value):
value = value.encode('utf-8') if type(value) is unicode else value
self.params[field] = value

def executable_client(self, is_post=False):
if is_post:
self.method = 'POST'
sorted_keys = [(x.lower(), x) for x in self.params.keys()]
sorted_keys.sort()
url_query = '&'.join(['{0}={1}'.format(sk[1], self.params[sk[1]])for sk in sorted_keys])
url = self.API_URL
if url_query:
url += '?{QUERY}'.format(
QUERY=url_query,
)
url_info = urlparse.urlparse(url)
timestamp = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f+08:00")
nonce = '%s%s' % (int(time.time() * 1000), random.randint(1000, 9999))
signature = self.get_signature(
timestamp=timestamp,
nonce=nonce,
url_info=url_info
)
headers = {
'X-Hmac-Auth-Timestamp': timestamp,
'X-Hmac-Auth-Version': self.API_VERSION,
'X-Hmac-Auth-Nonce': nonce,
'apiKey': self.API_KEY,
'X-Hmac-Auth-Signature': signature,
'X-Hmac-Auth-IP': self.ipaddress,
'X-Hmac-Auth-MAC': self.get_mac_address()
}
if is_post:
req = urllib2.Request(self.API_URL, urllib.urlencode(self.params), headers=headers)
else:
req = urllib2.Request(url, headers=headers)
try:
result = urllib2.urlopen(req).read()
print result
except urllib2.HTTPError, e:
print e.getcode()
print e.reason
print e.geturl()
print "-------------------------"
print e.info()
print e.read()


# if __name__ == "__main__":
# executableClient = ExecutableClient()
# print 'Get user by empId......'
# executableClient.add_params('loginWorkNo', '012662641024291838')
# print '=========================='
# print 'Get user by nickNameCn......'
# executableClient.add_params('processCode', u'Matter-36fab45e-d8be-4ed9-9e15-f6369e595fa3')
# executableClient.add_params('name', '012662641024291838')
# # GET
# executableClient.executable_client()
# # POST
# executableClient.executable_client(is_post=True)

php

SDK依赖

SDK-Demo:

📎epaasDemo.php


请注意:如果涉及多个参数,请对参数按照升序进行排序。

代码示例

<?php
/**
* handle collabration with BUC
* @author: piaoyu.ddh
* @date: 2015/5/10
* @version: 0.0.1
*/
class BucBase {
const API_SERVER = 'https://u-api.alibaba-inc.com';
const API_SERVER_DEV = 'http://u-api.alibaba.net';
const API_VERSION = '1.0';
const API_TIMEOUT = 3; # unit: second
private static $env = 'dev'; # prod by default
private static $nic = 'eth0'; # for multiple NIC host
//private static $nic = 'bond0'; # 阿里云实体机器的话要把这个静态变量初始化为 bond0 ,否则下面的getNicInfo 函数无法获取IP地址
private static $apiKey = 'MOCK-Ad$Un328d(t_4CE&6&0@Zve123';
private static $apiSecret = 'MOCKn0Ejx_Q$2MYU6f$_&Q406l$$Q2zC&d12345_';
/**
* init static members per config
* this function should be customized per app
*/
public static function init() {
// normally configuration could be loaded hereby
}
/**
* construct API signature
* @param Refer to <a href="http://gitlab.alibaba-inc.com/buc/buc-security-api/blob/master/Guide.md">API开发指南</a> for details
* @return signiture string
*/
protected static function getSignature($method,$timestamp,$nonce,$uri,$params) {
$bytes = sprintf("%s\n%s\n%s\n%s\n%s",$method,$timestamp,$nonce,$uri,$params);
$hash = hash_hmac('sha256',$bytes,self::$apiSecret,true);
return base64_encode($hash);
}
/**
* retrieve NIC configuration for IP & MAC
* Only linux system is supported so far
* @return array | false on error
*/
protected static function getNicInfo() {
$cmd = sprintf('/sbin/ifconfig %s|/usr/bin/head -2',self::$nic);
$output = `$cmd`;
if ( !$output ) {
return false;
}
$lines = explode("\n",$output);
$ret = array();
foreach( $lines as $line ) {
$tmp = array();
if ( preg_match('/HWaddr ((?:[0-9A-Fa-f]{2}:)+[0-9A-Fa-f]{2})/',$line,$tmp) ) {
$ret['mac'] = $tmp[1];
continue;
}
if ( preg_match('/inet addr:((?:[0-9]{1,3}.)+[0-9]{1,3})/',$line,$tmp) ) {
$ret['ip'] = $tmp[1];
continue;
}
}
return $ret;
}
/**
* construct server per configuration
* @return server string
*/
protected static function getServer() {
$server = self::API_SERVER;
if ( 'dev' == self::$env ) {
$server = self::API_SERVER_DEV;
}
return $server;
}
/**
* construct headers for BUC API request
* @param $api API path to be queried (w/o server part)
* @param $method HTTP method. So far only GET & POST are supported
* @param $params As it is. Key & value should be UTF-8 encoded w/o url encoding
* @return header array | false on error
*/
protected static function getHeaders($api,$method,$params) {
if ( !$api || !$method || !$params ) {
return false;
}
$addr = self::getNicInfo();
if ( !$addr ) {
return false;
}
$timestamp = strftime('%Y-%m-%dT%H:%M:%S.000+08:00');
$nonce = sprintf('%d000%d',time(),rand(1000, 9999));
ksort($params,SORT_STRING);
$ret = array();
foreach( $params as $k => $v ) {
$ret[] = sprintf('%s=%s',$k,$v);
}
$sig = self::getSignature($method,$timestamp,$nonce,$api,implode('&',$ret));
return array(
'X-Hmac-Auth-Timestamp' => $timestamp,
'X-Hmac-Auth-Version' => self::API_VERSION,
'X-Hmac-Auth-Nonce' => $nonce,
'apiKey' => self::$apiKey,
'X-Hmac-Auth-Signature' => $sig,
'X-Hmac-Auth-IP' => $addr['ip'],
'X-Hmac-Auth-MAC' => $addr['mac']
);
}
/**
* encapsulation for BUC getUserByLoginName
* @param $loginName The login name of the interesed user
* @return array
*/
protected static function getUserByLoginName($loginName) {
$api = '/rpc/enhancedUserQuery/getUserByLoginName.json';
$server = self::getServer();
$url = sprintf('%s%s',$server,$api);
$params = array('loginName' => $loginName);
$headers = self::getHeaders($api,'POST',$params);
return self::curlPost($url,$params,self::API_TIMEOUT,$headers);
}
/**
* helper function for HTTP POST request
*/
private static function curlPost($url, $params = array(), $timeout = 1, $headerAry = array()) {
$ch = curl_init( );
curl_setopt( $ch, CURLOPT_URL, $url);
curl_setopt( $ch, CURLOPT_POST, 1);
curl_setopt( $ch, CURLOPT_POSTFIELDS, $params);
curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt( $ch, CURLOPT_SSL_CIPHER_LIST, 'TLSv1');
curl_setopt( $ch, CURLOPT_HEADER, 0);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
if ($headerAry) {
$tmp = array();
foreach( $headerAry as $k => $v ) {
$tmp[] = sprintf('%s: %s',$k,$v);
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $tmp);
}
$data = curl_exec( $ch);
$error = curl_error( $ch );
curl_close( $ch );
return $data;
}
}
/**
* static member initializer
*/
BucBase::init();
/**
* BUC business logic for OPENDATA
*/
class BucModel extends BucBase {
/**
* get user detail by email prefix
* @param $prefix Email prefix for query
* @return false | array()
*/
public static function getUserDetail($prefix) {
return parent::getUserByLoginName($prefix);
}
}
BucModel::getUserDetail('piaoyu.ddh');

IOS

安装

  • 使用 CocoaPods(推荐)
pod 'ALBUCSecurityGatewayHTTPClient'

Demo

demo-1
[[ALBUCSecurityGatewayHTTPClient sharedSecurityGateway] setSecurityKey:@"Y02(n0Ejx_Q$2MYU6f$_&Q406l$$Q2zC&d4W6h$_"];
[[ALBUCSecurityGatewayHTTPClient sharedSecurityGateway] setApiKey:@"BUC-Ad$Un6h8d(t_4CE&6&0@ZveUry"];
[[ALBUCSecurityGatewayHTTPClient sharedSecurityGateway] postPath:@"/rpc/enhancedUserQuery/getUserByEmpId.json" parameters:@{ @"empId" : workId } success:
^(AFHTTPRequestOperation* operation, id responseObject) {
//do stuffs when success
}
failure:^(AFHTTPRequestOperation* operation, NSError* error) {
// do stuffs when fail
}];
demo-2

在命令行输入

git clone git@gitlab.alibaba-inc.com:xiaotian.lxt/ALBUCSecurityGatewayHTTPClient.git
cd ALBUCSecurityGatewayHTTPClient
pod install
open ALBUCSecurityGatewayHTTPClient.xcworkspace

运行即可.


备注:

  1. nodejs和C#暂不支持。

放接口服务规约与错误码说明

写在头里话

  • 网关只是透传请求,进行协议的转换,不涉及具体的业务逻辑,so,业务接口使用问题需要找接口提供方(从服务中心中搜索),找网关答疑同学是无法解答该类问题的。
  • 调用网关接口出错,请优先根据错误码自行排查问题,错误码请参考下文的说明,请仔细看一下错误信息,一般都能知道大致的原因。假如错误码是spInvokeErr,请优先找服务提供者排查问题

服务返回规约

应用服务通过网关对外开放服务时,服务返回内容必须严格遵守网关规范格式,你可以在网关控制台的服务配置中自定义自身服务和网关规范的字段映射关系,网关会根据你输入的字段名称从结果中尝试获取,所以:一定要匹配,一定要匹配,一定要匹配,否则网关无法正常返回结果。

推荐的返回结构

返回成功结构说明

{
success: true,
content: 返回内容,{}/[]
}

返回失败结构说明

{
success: false,
errorLevel:['info’, 'warn’, 'error’, 'fault’],
errorCode:错误码,
errorMsg:错误信息说明
}

网关失败时返回结构说明

{
"Code":"MissingParameter",
"HostId":"u-api.alibaba-inc.com",
"Message":"",
"_RequestId":"",
"errorCode":100,
"errorLevel":"error",
"errorMsg":"",
"success":false
}

登录态失败时返回结构说明

{
"AccessToken":"",
"IsAccess":false,
"errorCode":1206,
"errorLevel":"error",
"errorMsg":"",
"success":false
}

网关错误返回结构码说明

Code错误码字段说明

错误码

错误描述

解决方案

MissingParameter

请求入参丢失

参考api文档的请求参数中必填列的说明

InvalidAPIKey

access_key不合法

请使用网关发布的应用access_key

InvalidAPIKeyID.NotFound

access_key不存在

确认使用的access_key与应用环境是否匹配,区分日常与线上

InvalidAPI.NotFound

访问的API服务不存在

确认访问的API服务名是否真实存在,一般服务名以/开头

Forbidden

应用未开通服务的访问权限

需先订阅开通服务的访问权限

NoSuchVersion

服务版本不匹配

从服务详情确认服务提供的版本号,默认1.0

SignatureNonceUsed

相同请求短时间内重复拒绝

请确认客户端代码是否存在请求重复提交

UnsupportedOperation

请求模式不匹配

从服务详情确认API服务接受的HTTP提交模式

SignatureDoesNotMatch

签名结果不符合网关服务标准

先确认签名的secret_key是否正确,然后按照文档服务消费->代码开发章节的加签说明

FlowCtrlBanned

服务触发限流后被禁用

等待禁用时间结束,服务会自动恢复

FlowCtrlHitMaxCount

服务触发限流

服务在限流规则范围内达到请求最大阈值

SPParamMiss

后端服务参数缺失

必须参数未配置为必填

spInvokeErr

后端服务异常失败

网关往后端传递请求时失败

IpWhiteListValidate

客户端IP不在服务设定白名单内

请确认客户端是否是合法范围内使用者

InvalidTimeStamp

客户端时间戳非法

网关服务端最大接受客户端时间误差在15分钟

OauthInvokeError

sso登录态验证失败

SSO验证登录态服务异常

SecurityValidate

http请求方式不匹配

更换请求方式POST/GET

InvalidSPKeyID.NotFound

后端应用服务未提供或者服务未开启

练下提供方检查应用服务是否被删除或下线

errorCode错误码字段说明

错误码

错误描述

100

网关内部发生错误,详情参考Code字段说明

1006

当前用户没有访问该应用的权限

1016

您的密码已过期,请重置密码后使用新密码登录

1019

无效的参数(比如不允许为空的参数空了)

1020

请求已过期

1021

签名验证失败

1200

AppCode非法

1205

请求ValidateAccessToken时参数为空

1206

AccessToken无效

1207

AccessToken过期

1208

根据AccessToken取不到UserProfile信息

1209

获取AccessToken的授权方式未在受保护的API中进行授权

1210

获取AccessToken的授权方式低于Api保护的授权

1211

无效的客户端类型

1220

刷新accessToken失败,accessToken已经被更新

1222

应用信息不准确,accessToken不是当前应用生成

1230

appCode和token关联的不相同

上述errorCode为网关占用错误码,业务系统建议数字型错误码5位数开始或用字符串型,如果业务应用错误码与系统保留雷同,网关会在业务错误码增加x-前缀后对外输出。

应用服务返回结果不符合规范的修改建议与示例

  • 服务返回结果没有包装类,建议单独定义API,返回结果按格式规约进行包装。
  • 服务返回结果已经包装,但是字段没有全部匹配,可以在网关控制台应用服务配置页面进行自行补充格式。举例说明
    如果应用服务返回的数据格式如下
{
"success":true,
"data":{
"success":true,
"content":{},
"errorLevel":"",
"errorCode":"",
"errorMsg":""
},
}


控制台上配置方法如下

规范字段名

服务字段名

success

data.success

content

data.content

errorLevel

data.errorLevel

errorCode

data.errorCode

errorMsg

data.errorMsg

如果应用服务返回的数据格式如下
{
"isSuccess":true,
"content":{},
"code":"",
"message":""
}
控制台上配置方法如下

规范字段名

服务字段名

success

isSuccess

content

content

errorLevel

errorLevel

errorCode

code

errorMsg

message

常见问题

  1. FlowCtrlHitMaxCount 服务限流,服务在限流规则范围内达到请求最大阈值 最大阈值为:单个接口 50qps

回答:单个接口 50qps

Copyright © 2024钉钉(中国)信息技术有限公司和/或其关联公司浙ICP备18037475号-4