## 一、概述
提供外部自建系統的代理商,可以通過接口服務,實現對方系統與我方系統之間的技術銜接;

## 二、參考
>[danger]
> 1、在本系統里面,無需獨立的去開放平臺開戶,創建頂級代理商的時候,就自動開戶了,也就可以獲得相關參數了;
> 2、需要注意,需要在開放服務模塊,針對當前代理商的客戶端,設置允許訪問的IP白名單;
> 3、調用的api,需要對客戶端做授權;
## 三、整體流程
創建接口開放服務賬號->獲取接口開放服務參數->第三方編寫程序->測試->上線;

### **客戶端請求實例代碼**
```
public class ClientSample
{
public static void main(String[] args)
{
Log4jV2Util.initLog4jV2TestEnv();
String hostUrl = "http://{hosturl}/api/rayoauth";
String apiUrl = "/query/card";
String appId = "ray40c9903c1";
String appSecret = "46bacebf-f63c-41cc-b29c-5812994a5e83";
Map<String, String> paramap = new HashMap();
paramap.put("userCode", "13313312");
String result = OauthCaller.call(hostUrl, apiUrl, appId, appSecret, paramap);
Logger logger = LoggerFactory.getLogger(OauthClientTest.class);
logger.debug(result);
}
}
```
OauthCaller:
```
public class OauthCaller
{
public static String call(String oauthHostUrl, String servceUrl, String appId, String appSecret, Map<String, String> businessParaMap)
{
Map<String, String> allParaMap = new HashMap();
Map<String, String> headmap = new HashMap();
headmap.put(RayOauthServerConstants.rayOauthHeadAppId, appId);
headmap.put(RayOauthServerConstants.rayOauthHeadTimeStamp, String.valueOf(new Date().getTime()));
allParaMap.putAll(businessParaMap);
allParaMap.putAll(headmap);
headmap.put(RayOauthServerConstants.rayOauthHeadSignature, Sign.generateSign(allParaMap, appSecret));
String result = null;
try
{
result = HttpPostUtil.executeActionWithHead(oauthHostUrl + servceUrl, businessParaMap, headmap, "", false);
}
catch (Exception e)
{
e.printStackTrace();
}
return result;
}
}
```
HttpPostUtil.executeActionWithHead:
```
public static String executeActionWithHead(String postUrl, Map<String, String> postParaMap, Map<String, String> headParaMap, String paraContentType, boolean trace) throws Exception
{
BasicCookieStore cookieStore = new BasicCookieStore();
try (final CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(connectionConfig()).setDefaultCookieStore(cookieStore).setDefaultRequestConfig(requestConfig()).build();)
{
String result = null;
try
{
ClassicRequestBuilder requestBuilder = ClassicRequestBuilder.post().setUri(postUrl);
if (postParaMap != null)
{
Iterator paramIterator = postParaMap.entrySet().iterator();
while (paramIterator.hasNext())
{
Map.Entry entry = (Map.Entry) paramIterator.next();
requestBuilder.addParameter(new BasicNameValuePair((String) entry.getKey(), (String) entry.getValue()));
}
}
if (headParaMap != null)
{
Iterator headIterator = headParaMap.entrySet().iterator();
while (headIterator.hasNext())
{
Map.Entry<String, String> entry = (Map.Entry) headIterator.next();
requestBuilder.addHeader(entry.getKey(), entry.getValue());
}
}
if (!StringUtils.isEmpty(paraContentType))
{
requestBuilder.setHeader("Content-type", paraContentType);
}
ClassicHttpRequest request = requestBuilder.build();
try (final CloseableHttpResponse response = httpclient.execute(request))
{
result = EntityUtils.toString(response.getEntity());
}
}
catch (Exception ex)
{
logger.error("executeAction執行中發生了異常,堆棧如下:", ex);
}
return result;
}
}
```
## 四、技術要求
1.所有接口的請求,采用httpPost方式提交,發出http請求的時候,請求content-type需設定為:application/x-www-form-urlencoded;charset=UTF-8 ,參數按照普通表單參數傳值,不必包裹成一個json字符串;
2.所有接口的響應,采用json格式,編碼為UTF-8,響應的content-type為:application/json;charset=UTF-8);
3.提供ip白名單功能,僅供授權的ip調用;
4.每個對外提供服務的api,都必須在請求頭部包含參數rayOauthServerAppId及rayOauthServerTimeStamp,為了確保不被外部不法分子惡意篡改請求,請求參數中絕對不能包含appSecret;
5.每個對外提供服務的api,都必須在請求頭部包含一個rayOauthServerSignature參數,該參數是通過算法計算出來的值,具體算法參考后續簽名算法章節;
6.考慮服務器承載量,目前限制每個ip訪問接口頻率不得超過1秒10次,超過這個次數,該ip將會被禁止調用,程序中不得做高頻率輪詢;
## 五、時間戳格式
1.獲取從1970, 00:00:00開始到當前的毫秒數(13位) ;
2.每個api請求的有效時間為3分鐘,所以,務必確保當前服務請求的時間是準確的;
## 六、rayOauthServerSignature簽名算法
假設:rayOauthServerSignature= 當前api的所有參數(除rayOauthServerSignature外),根據 **參數名升序** 排序,以“**`參數名=值&`**”的方式連接起來,最后一個參數不需要&符號,以值結尾;
結果:rayOauthServerSignature= md5(md5(paramstrings)+appSecret)
> 注意:
> 1、是雙層md5(32位小寫)加密,中間那個"+"僅表示兩段字符相連,并不包括該符號本身;
> 2、參數包括了請求頭和請求體里面的所有參數;
## 七、測試案例
