# **API架構**
AWS 對每次API請求都進行一次完整的安全驗證,驗證處理過程與開發者選擇的API技術協議有關(如選擇HTTP或SOAP)。下圖是一個抽象的API架構圖:

**請求處理過程**
1. 發起請求
2. 因安全或請求非法被拒絕
3. 調用AWS PaaS的本地接口封裝
4. 將處理結果以JSON/XML文檔返回
# **API接入方法**
## **1. HTTP(s) API接入**
HTTP(s) API又叫Web API,開發者可以通過HTTP形式發起請求,獲取JSON或XML的處理結果。由于HTTP被廣泛的編程語言支持,開發者可以基于此HTTP形式與各種語言的企業應用進行交互。
在調用這類API之前,開發者需要向管理員申請API密鑰,該秘鑰由AWS CC的秘鑰身份進行管理。包括訪問憑證 ( access_key ) 和 私鑰 ( secret )。
access_key將作為參數包含在每一個請求中發送,而secret負責生成請求串的簽名,**secret需要妥善保管,請勿外傳。**
### 1.1 技術規格
項|說明
--|--
訪問協議|HTTPS/HTTP
提交方式|POST/GET,推薦POST
業務參數|JSON
返回結果|JSON/XML
編碼格式|UTF-8
### **1.2 API請求結構**
項|說明
--|--
API入口|Portal URL + /openapi,例如:https://b2b.awspaas.com/openapi
公共參數|- cmd api名稱(必須)<br>- access_key 訪問憑證(必須)<br>- sig 請求消息的簽名(必須),<b>詳見下說明</b><br>- sig_method 簽字算法,支持:HmacMD5<br>- format 處理結果數據格式。支持json和xml,默認為json<br>- timestamp 請求時間,long型毫秒值,默認和服務器事件不能超過6分鐘
業務參數|一個json串,見相關API的說明文檔
### **1.3 API請求樣例及接入說明**
一個典型的API請求如下所示,這是一個 app.install.check 的API請求
```
https://b2b.awspaas.com/openapi
?timestamp=1439277618461
&sig_method=HmacMD5
&cmd=app.install.check
&appId=com.actionsoft.apps.notification
&access_key=Salesforce#1
&format=json
&sig=DE90336BEDB0C3D3FE6DEE2FF0DF11AC
```
### **1.4 API返回結構**
由請求參數format指定的數據格式,默認為json串。例如在Java客戶端中,該參數運用如下:
```
//返回JSON結構
OpenApiClient client = new OpenApiClient(apiServer, accessKey, secret);
//或者
OpenApiClient client = new OpenApiClient(apiServer, accessKey, secret,
OpenApiClient.FORMAT_JSON);
//返回XML結構
OpenApiClient client = new OpenApiClient(apiServer, accessKey, secret,
OpenApiClient.FORMAT_XML);
```
項|說明
--|--
result|狀態碼。ok代表成功,error代表失敗
errorCode|錯誤碼
msg|結果信息。如果result值為error時,提供錯誤描述信息
data|業務數據
### **1.5 API返回樣例**
一個典型的API請求返回結果如下所示
這是一個 app.install.check API的JSON結果
```
{
"data" : true,
"result" : "ok"
}
```
這是一個 app.install.check API的XML結果
```
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<boolResponse>
<data>true</data>
</boolResponse>
```
### **1.6 接入調用說明**
#### 1.獲取秘鑰
開發者在完成每次HTTP API調用前,API發起的URL中需要帶上sig參數。sig參數是秘鑰access_key和secret及參數串的簽名。秘鑰由AWSCC的秘鑰身份進行管理,開發者可以向管理員申請API密鑰,包括訪問憑證 ( access_key ) 和 私鑰 ( secret )。

#### 2.使用AWS HTTP SDK for Java API工具包(Java語言可使用此方法,其他語言見 第三點)
這是我們為Java開發者提供的HTTP API客戶端開發工具包(AWS HTTP SDK for Java API工具包),可以簡化對HTTP API的使用,也無需考慮URL簽名過程。(依賴jar文件可在調用時聯系管理員獲取)
jackson類庫用于處理json到Java對象的轉換處理。建議使用AWS PaaS平臺默認提供的相關資源:
```
* webserver/webapps/portal/WEB-INF/lib/jackson-core-2.4.1.jar
* webserver/webapps/portal/WEB-INF/lib/jackson-databind-2.4.1.jar
* webserver/webapps/portal/WEB-INF/lib/jackson-module-jaxb-annotations-2.5.2.jar
* webserver/webapps/portal/WEB-INF/lib/jackson-annotations-2.4.0.jar
```
##### 2.1 JAVA客戶端示例代碼
**OpenApiClient構造函數**
```
//返回JSON結果
OpenApiClient client = new OpenApiClient(apiServer, accessKey, secret,OpenApiClient.FORMAT_JSON);
//返回XML結果
OpenApiClient client = new OpenApiClient(apiServer, accessKey, secret,OpenApiClient.FORMAT_XML);
//返回對象結果(不同API封裝的結果對象不同,見示例代碼)
OpenApiClient client = new OpenApiClient(apiServer, accessKey, secret);
BoolResponse r = client.exec(apiMethod, args, BoolResponse.class);
```
**示例**
```
//API入口
String apiServer = "https://b2b.awspaas.com/openapi";
String accessKey = "Salesforce#1";
String secret = "0a799959-8327";//注意保密
String apiMethod = "bo.query";//API方法,cmd參數
//API請求參數
Map<String, Object> args = new HashMap<String, Object>();
args.put("boName","BO_ACT_TEST" );
//構建客戶端
OpenApiClient client = new OpenApiClient(apiServer, accessKey, secret);
//執行并獲得查詢結果
ListMapResponse r = client.exec(apiMethod, args, ListMapResponse.class);
List bos = r.getData();
if (bos != null) {
for(int i=0;i<bos.size();i++){
//...
}
}
```
#### 3.通過簽名URL請求,完成API調用(其他語言)
簽名的目標是由開發者在API客戶端計算出系列參數組合的哈希值,將產生的信息添加到URL請求的sig參數。
##### 3.1 獲得秘鑰
這里介紹API請求中簽名 ( sig ) 的生成方法。簽名需要開發者先在控制臺創建API密鑰 ,獲得訪問憑證 ( access_key ) 和私鑰 ( secret ),這里我們假設
```
access_key = 'Salesforce#1'
secret = '0a799959-8327'
```
##### 3.2 計算簽名
例如API請求參數如下(這是一個 app.install.check API):
```
{
"timestamp":"1439279383630",
"sig_method":"HmacMD5",
"cmd":"app.install.check",
"appId":"com.actionsoft.apps.notification",
"access_key":"Salesforce#1",
"format":"json"
}
```
###### 1. 按參數名進行升序排列
準備參數。范圍:cmd,access_key,timestamp,format,sig_method和業務參數(見API輸入參數文檔),其中不包括空值參數排序后的參數為:
```
{
"access_key":"Salesforce#1",
"appId":"com.actionsoft.apps.notification",
"cmd":"app.install.check",
"format":"json",
"sig_method":"HmacMD5",
"timestamp":"1439279383630"
}
```
###### 2. 構造簽名串
以secret字符串開頭,追加排序后參數名稱和值,格式:secretkey1value1key2value2...
應用到上述示例得到簽名串為(注意:簽名串中間沒有空格分割):
```
0a799959-8327access_keySalesforce#1appIdcom.actionsoft.apps.notificationcmdapp.install.checkformatxmlsig_methodHmacMD5timestamp1439277618461
```
###### 3. 計算簽名
計算被簽名串加密的簽名。將API密鑰的私鑰 (secret) 作為key,生成被簽名串的 HmacMD5簽名
將簽名得到的16字節依次轉化為大寫的16進制字符串,如果字符串長度為1,在前補0,結果為32位字符串,例如:050CC7A1C04487EAE1197C31D28B7E37
###### 4. 添加簽名
將計算的簽名值以sig參數名,附加到URL請求中。一個典型的API請求如下所示
這是一個 app.install.check 的API請求
```
https://b2b.awspaas.com/openapi?timestamp=1439277618461
&sig_method=HmacMD5
&cmd=app.install.check
&appId=com.actionsoft.apps.notification
&access_key=Salesforce#1
&format=json
&sig=DE90336BEDB0C3D3FE6DEE2FF0DF11AC
```
## **2.SOAP API接入**
SOAP API即Web Service。開發者可以通過HTTP傳輸協議發送SOAP格式的請求消息獲得XML結構的處理結果。
在調用這類API之前,開發者需要向管理員申請API密鑰,該秘鑰由AWS CC的秘鑰身份進行管理。包括用戶名和密碼及增強安全策略。
用戶名和密碼的傳輸需要遵循WS-Security的用戶名密碼類型令牌規范,**密碼需要妥善保管,請勿外傳**。
### **2.1 技術規格**
項|說明
--|--
Transports|HTTPS/HTTP
JSR|Java API for XML-Based Web Services (JAX-WS) 2.0 - JSR-224<br>Web Services Metadata for the Java Platform - JSR-181<br>SOAP with Attachments API for Java (SAAJ) - JSR-67
WS-*和相關規范|WS-I Basic Profile 1.1<br>WS-Reliable Messaging<br>WSDL 1.1<br>WS-Security<br>SOAP 1.1, SOAP 1.2<br>Message Transmission Optimization Mechanism (MTOM)<br>JAXB 2.x
### **2.2 API請求結構**
項|說明
--|--
API入口|Portal URL + /openapi,例如:https://b2b.awspaas.com/openapi
查詢參數|- service 值為服務ID(必須),例如appApi<br>- wsdl true/false,是否返回WSDL(可選)
業務參數|一個json串,見相關API的說明文檔
### **2.3 API請求樣例及接入說明**
一個典型的API請求如下所示,這是一個 isInstalled 的SOAP API請求
```
<soap:Envelope xmlns:ser="http://service.sdk.actionsoft.com/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"/>
<soap:Body>
<ser:isInstalled>
<appId>com.actionsoft.apps.notification</appId>
</ser:isInstalled>
</soap:Body>
</soap:Envelope>
```
### **2.4 API返回結構:**
項|說明
--|--
result|狀態碼。ok代表成功,error代表失敗
errorCode|錯誤碼
msg|結果信息。如果result值為error時,提供錯誤描述信息
data|業務數據
### **2.5 API返回樣例**
一個典型的SOAP API請求返回結果如下所示
這是一個 isInstalled 的SOAP API服務返回結果
```
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns1:isInstalledResponse
xmlns:ns1="http://service.sdk.actionsoft.com/">
<return
xmlns:ns2="http://service.sdk.actionsoft.com/">
<data>true</data>
</return>
</ns1:isInstalledResponse>
</soap:Body>
</soap:Envelope>
```
### **2.6 接入調用說明**
#### 1.獲取秘鑰
秘鑰由AWS CC的秘鑰身份進行管理,開發者可以向管理員申請API密鑰,包括用戶名和密碼。

#### 2.使用SoapUI工具調用
SoapUI是一個開源測試工具,希望快速熟悉SOAPAPI的開發者,也可以使用這個客戶端測試每個API。
**場景**
調用appApi服務的isInstalled方法判斷AWS PaaS是否安裝了某個應用。假定SOAP安全策略配置為用戶名密碼認證、密碼加密傳輸
```
用戶名:Salesforce#1
密碼:0a799959-8327
```
**步驟**
**1.創建soapUI工程,File > new soapUI Project**

**2.配置參數及認證用戶名密碼**


**3.運行查看結果**

#### 3.使用CXF客戶端調用
**場景**
調用appApi服務的isInstalled方法判斷AWS PaaS是否安裝了某個應用。假定服務端配置用戶名密碼認證、密碼加密傳輸
```
用戶名:Salesforce#1
密碼:0a799959-8327
```
**完整的CXF示例代碼**
```
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.handler.WSHandlerConstants;
import com.actionsoft.sdk.service.AppApi;
import com.actionsoft.sdk.service.AppApiPortType;
import com.actionsoft.sdk.service.BoolResponse;
public class AppApiClient {
public static void main(String[] args) {
// 構造攔截器,設置用戶名密碼屬性和認證類型
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION,
WSHandlerConstants.USERNAME_TOKEN);
//outProps.put(WSHandlerConstants.PASSWORD_TYPE,
WSConstants.PW_TEXT);//密碼明文還是密文,不設置時默認密文
outProps.put(WSHandlerConstants.USER, "Salesforce#1");
outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new UTSetPasswordCallback());
WSS4JOutInterceptor interceptor = new WSS4JOutInterceptor(outProps);
// 注入cxf的攔截器增加用戶名密碼
AppApi api = new AppApi();
AppApiPortType port = api.getAppApiPort();
Client client = ClientProxy.getClient(port);
client.getOutInterceptors().add(interceptor);
// 服務方法調用
BoolResponse r = port.isInstalled("com.actionsoft.apps.notification");
// 業務異常判斷
if (r.getErrorCode() == null) {
System.out.println(r.isData());
} else {
System.out.println(r.getErrorCode() + "," + r.getMsg());
}
}
private static class UTSetPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
if ("Salesforce#1".equals(pc.getIdentifier())) {
pc.setPassword("0a799959-8327");
return;
}
}
}
}
}
```
**Simple Frontend Client Proxy(CXF私有調用方式)**
```
import org.apache.cxf.frontend.ClientProxyFactoryBean;
...
ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
factory.setServiceClass(AppApiPortType.class);
factory.setAddress("http://localhost:8088/portal/r/s?service=appApi");
AppApiPortType client = (AppApiPortType) factory.create();
```
#### 4. 使用JDK自帶WebService框架
**注意:**
1. JDK1.6或更高版本才提供Web Service實現。
2. 目前JDK對WS-*規范的支持不夠完善,例如發送WS-Security規范要求的用戶名密碼時需要借助其它服務框架,因此本章沒有安全驗證。(AWS PaaS提供的官方SOAP API要求必須有用戶名密碼驗證,因此本章節調用方式不支持由AWS PaaS提供的官方SOAP API)
**JAX-WS Proxy方式調用服務步驟**
1. 準備JDK1.6或以上版本
2. 生成服務客戶端文件
使用JDK bin目錄下的wsimport命令生成服務客戶端文件,如果不指定。示例:
```
wsimport -keep -clientjar app_service_client.jar "http://localhost:8088/portal/r/s?service=appApi&wsdl=true"
```
3. 獲得服務和代理對象
在生成的app_service_client.jar中有2個重要的類,如下:
項|說明
--|--
AppApi|AppApi是服務類,繼承javax.xml.ws.Service,它的名字默認和wsdl中服務名稱對應,wsdl:service的name屬性,該類用于創建服務對象實例
AppApiPortType|AppApiPortType類是該服務的SEI(service endpoint interface),可以得到代理對象實例
AppApi的構造函數中可以指定3個參數:
項|說明
--|--
java.net.URL wsdlDocumentLocation|WSDL路徑,默認使用app_service_client.jar中備份的生成源WSDL
QName serviceName | 該參數指定調用哪個服務
WebServiceFeature... features | 服務的特殊功能或者處理,例如MTOMFeature可以指定是否優化附件傳輸
通過生成的服務類獲得代理對象的代碼示例:
```
AppApi api = new AppApi();
AppApiPortType port = api.getAppApiPort();
```
通過javax.xml.ws.Service和SEI得到代理對象:
```
URL wsdlURL = new URL("http://localhost:8088/portal/r/s?service=appApi&wsdl=true");
QName SERVICE_NAME = new QName("http://service.sdk.actionsoft.com/", "AppApi");
Service service = Service.create(wsdlURL, SERVICE_NAME);
AppApiPortType client = service.getPort(AppApiPortType.class);
```
4. 通過代理對象調用服務
```
AppApiPortType client = service.getPort(AppApiPortType.class);
BoolResponse result = client.isInstalled("com.actionsoft.apps.notification");
```
**JAX-WS Dispatch方式調用服務步驟**
1. 準備JDK1.6或以上版本
2. 獲得服務和Dispatch對象
3. 通過Dispatch對象調用服務
```
import java.net.URL;
import javax.xml.transform.Source;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
...
URL wsdlURL = new URL("http://localhost:8088/portal/r/s?service=appApi&wsdl=true");
Service service = Service.create(wsdlURL,
new QName("http://service.sdk.actionsoft.com/", "AppApi"));
Dispatch<Source> disp = service.createDispatch(
new QName("http://service.sdk.actionsoft.com/",
"AppApiPort"), Source.class, Service.Mode.PAYLOAD);
Source request = new StreamSource("<appId>com.actionsoft.apps.notification</appId>")
Source response = disp.invoke(request);
```
- OpenAPI使用方法
- API調用案例
- 使用Java客戶端
- 使用其他語言(簽名URL請求)
- 組織機構接口
- 單位
- 新建單位
- 更新一個單位信息
- 注銷單位,注銷后不在顯示該單位及單位下的部門和人員
- 激活注銷的單位
- 獲得一個單位對象(根據單位id)
- 獲得單位列表
- 刪除單位
- 獲得用戶所有兼職單位(單用戶)
- 獲取用戶所有兼職單位(多用戶)
- 獲得一個單位對象(根據賬戶)
- 判斷某用戶對于某單位是否有AC權限
- 獲取指定單位層級下的部門
- 部門
- 按部門查詢用戶對象
- 獲取部門下兼職的用戶對象
- 按部門查詢用戶對象(包含兼職)
- 獲取部門下用戶數
- 按部門查詢有管理者身份的人
- 是否有部門管理者身份
- 獲得部門兼任列表
- 新建部門
- 獲得指定部門所在的行政區域劃分,若果當前部門沒有設定則自動向上尋找
- 合并指定部門到另一個部門
- 合并多個部門到目標部門(注意,僅合并指定的源部門,不包含子部門)
- 移動一個部門位置到單位下(id不變),該部門下的子部門layer也會級聯改變
- 移動一個部門位置(id不變),該部門下的子部門layer也會級聯改變
- 獲得部門對象
- 是否存在子部門
- 獲得指定部門的下級部門
- 獲得一個部門對象
- 更新部門
- 注銷部門,注銷后不在顯示該部門及子部門和人信息
- 激活注銷的部門
- 刪除部門
- 賬戶
- 獲得一個用戶對象
- 通過id獲得一個用戶對象
- 將可能含有賬戶別名的字符串處理成合法的登錄賬戶名
- 將可能含有多個賬戶別名的字符串處理成合法的登錄賬戶名字符串
- 將可能含有多個賬戶別名的字符串處理成用戶對象
- 將一組AWS登錄賬戶轉換成友好的賬戶別名
- 通過split將一組AWS登錄賬戶(或含有別名的賬戶)轉換成姓名
- 將一組AWS登錄賬戶(或含有別名的賬戶)轉換成姓名
- 檢查一組AWS登錄賬戶合法性,將不合法的賬戶返回
- 在uid所能訪問的所有賬戶范圍中搜索用戶,返回匹配登錄名、姓名或姓名拼音首字母結果
- 創建賬戶
- 更新一個賬戶屬性
- 注銷賬戶
- 激活賬戶
- 刪除賬戶(根據賬號)
- 賬戶移動部門
- 設置管理者身份
- 初始化用戶密碼
- 重設用戶密碼
- 刪除用戶(根據uniqueid)
- 更新用戶角色
- 取消管理者身份
- 創建一個賬戶兼任信息
- 查詢用戶兼任列表
- 刪除兼任
- 根據用戶姓名和手機獲取用戶對象
- 角色
- 按角色查詢用戶對象
- 按角色查詢用戶對象,包含兼職到參數指定的角色用戶
- 創建一個新角色
- 刪除角色
- 更新角色
- 獲得角色列表
- 獲得角色
- 獲得一個角色對象
- 通過分類名獲得角色列表
- 通過賬戶名獲得一個角色對象
- 團隊
- 新建團隊
- 更新團隊名稱
- 移除團隊小組
- 獲得團隊列表
- 獲得團隊成員
- 添加團隊小組成員
- 刪除團隊成員