开放接口 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();
- 如果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:
请注意:如果涉及多个参数,请对参数按照升序进行排序。
代码示例
<?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
运行即可.
备注:
- 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 |
常见问题
- FlowCtrlHitMaxCount 服务限流,服务在限流规则范围内达到请求最大阈值 最大阈值为:单个接口 50qps?
回答:单个接口 50qps。