做過開發的程序猿,基本都寫過接口,寫接口不算難事,與接口交互的對象核對好接口的地址、請求參數和響應參數即可,我在作為面試官去面試開發人員的時候,有時候會問這個問題,但相當多的一部分人并沒有深入的考慮過怎么寫好一個接口,包括接口的可靠性、安全性和高并發等等。
小編在項目開發過程中,與很多的企業對接過接口,包括一些小企業定制化的軟件接口,也包括一些大廠形成規范的接口,比如支付寶、銀聯、微信,以及各大銀行等等,相對來說,很多小廠的接口編寫比較隨意,而大廠對接口的定義就比較嚴謹,接下來我們就來分析一下。
1、api接口簽名
有過開發經驗的程序猿,一般都會注意到這個問題,為了接口的安全性,需要對參數進行簽名,防止參數在請求過程中被篡改。就類似于你的一個好久沒聯系的朋友或者同學,通過微信或者QQ,給你發了條消息,向你借錢,有一部分人的第一反應是他是不是被盜號了?他是本人嗎?所以我們建議通過視頻或者電話再溝通核實一遍,這就是為了安全性防止財產的損失。
(1)為什么需要 API 接口簽名?
對外開放的 API 接口都會面臨一些安全問題,例如偽裝攻擊、篡改攻擊、重放攻擊以及數據信息泄露的風險。利用 API 接口簽名能方便的防范這些安全問題和風險。在設計 API 接口簽名時主要考慮以下幾點:
- 保證請求數據正確
當請求中的某一個字段的值變化時,原有的簽名結果就會發生變化。所以,只要參數發生變化,簽名就要發生變化,否則請求將會是一個無效的請求。
- 保證請求來源合法
一般情況下,生成簽名的算法都會成對出現一個 AppKey 和一個 appSecret,根據 appKey 能識別出調用者身份;根據 appSecret 能識別出簽名是否合法。
這兩個參數是非必須的,根據平臺的商戶定,比如如果平臺沒有商戶的概念或者只有一個商戶,像我們常見的自己的客戶端對接自己的服務端。但是像一些提供接入能力的平臺,比如微信,很多企業都可以接入,那他就需要區分不同的商戶,每個商戶分配一個appId,然后再通過appKey+appSecret來區分調用者的權限。
- 識別接口的時效性
一般情況下,簽名和參數中會包含時間戳,這樣服務端就可以驗證客戶端請求是否在有效時間內,從而避免接口被長時間地重復調用。
(2)簽名實現邏輯
各家在簽名時的請求參數可能有一點差別,但整體實現邏輯是類似的。
第一步:將除sign外的所有請求參數放入集合Map中;
第二步: 將第一步得到的集合M中的參數按照參數名ASCII碼從小到大排序(字典序);
第三步:將第二步中排序后的key和與之對應的value拼接起來,使用URL鍵值對的格式(即key1=value1&key2=value2…)得到字符串 params;
第四步:在params的最后再拼接appKey密鑰,然后通過某種加密算法或通過hash算法得到 sign 值(一般為Base64(Hmac_SHA1(params, appSecret)));
第五步:sign加到params 中,將params放入請求頭中發送請求目標接口;
(3)簽名實現代碼
我們會常用到幾個參數:
version:版本號
charset :字符編碼
sign_type:簽名類型,如RSA、RSA2、SHA
timestamp:當前時間戳,格式YYYYMMDDhhmmss,在服務端會驗證比對請求方的時間與系統當前時間差,若超出太多,比如超過十分鐘,就認為這個是過期的請求,不允許獲取數據。
nonce_str:隨機字符串,隨機以主要保證簽名不可預測。每次請求時都不一樣。
2、加密算法
對于一些比較敏感的私人信息,接口傳輸過程最好進行加密傳輸,比如用戶姓名、手機號、身份證號碼等。如果需要明文保存,可以使用AES雙向加密解密,如果直接保存加密的數據,可以使用md5加密。
(1)md5加密
MD5加密是一種不可逆的加密算法,不可逆加密算法的特征是加密過程中不需要使用密鑰,輸入明文后由系統直接經過加密算法處理成密文,這種加密后的數據是無法被解密的,只有重新輸入明文,并再次經過同樣不可逆的加密算法處理,得到相同的加密密文并被系統重新識別后,才能真正解密。很多網站為了安全性,會額外再加一個鹽值(一串隨機字符串)一起進行加密,提高加密的安全性。MD5鹽值加密作用就是為了防止MD5被暴力破解。通過鹽值和密碼進行組合計算得出MD5,在數據庫中要同時存儲該MD5碼及鹽值。在需要驗證密碼正確性時,可以將密碼和數據庫中對應賬號密碼的鹽值組合計算出的MD5與數據庫中的MD5進行比對。
另外一些網站號稱可以對md5加密的密文進行解密,他們只是手機了足夠多的密碼信息,來進行匹配,反向推導出加密前的明文,實際上是無法真正的實現md5解密的。
(2)AES雙向對稱加解密
AES是一種分組密碼 分組長度為128位(16字節),根據密鑰長度可分為AES-128 AES-192和AES-256,密鑰長度不同,AES的加密輪數也不同。
AES的工作模式分為ECB、CBC、CFB等
ECB是最簡單和最早的模式,首先是密鑰擴展,將加密的數據按照16字節的大小分成若干組,對每組都用同樣的密鑰加密。
CBC和ECB的區別就是添加了一個初始向量(16字節),在將密鑰分成若干組之后,第一組與初始化向量異或之后再進行與ECB相同的加密流程,后面的每一組都與上一組的密文進行異或之后再與密鑰加密。
3、編碼解碼
(1)為什么會出現亂碼
在計算機中,不管是一段文字、一張圖片還是一段視頻,最終都是以二進制的方式來存儲。也就是最終都會轉化為 0001 1000 這樣的格式。換句話說,計算機只認識 0 和 1 這樣的數字,并不能直接存儲字符。所以我們需要告訴它什么樣的字符對應的是什么數字。但是如果用戶A定義的編碼規則,傳到B使用了另外一套解碼規則進行解碼,由于編碼和解碼的規則對應不上就導致了亂碼。
(2)怎么解決亂碼
這就需要我們了解JAVA中的編碼轉換。雙方定義好編碼方式,我們以utf8和gbk這兩種常見的編碼為例。
4、接口token鑒權
有一部分人會把token鑒權和接口加簽搞混,我們來梳理一下這兩種方式。
使用Sign簽名,是為了對接口參數進行驗證,我們在業務開發過程中與上下游系統進行接口對接時,常采用這種方式,那為什么不是token方式呢,因為我們不需要登錄上下游系統,他們在攔截器層面已經放行了這些接口,不需要登錄后才給調用。而像我們管理平臺上的接口,比如查詢某個列表的數據,就不能直接調用接口,需要先登錄系統才有調用接口的權限。
Token是用來判斷用戶身份的一個標識,身份驗證通過了,才能調用接口,token也是一種加密方式,使用用戶的信息進行加密,一般可以使用md5加密,我們來看一下token鑒權的過程。
第一步:用戶使用賬號密碼登錄,向服務端發起http請求
第二步:服務端校驗賬號密碼數據
第三步:驗證成功后,服務端會生成一個token,并將這個token返回給客戶端
第四步:客戶端收到這token后將它放在cookie或者本地存儲
第五步:客戶端再次向服務器發起請求時帶上token
第六步:服務端收到請求,然后驗證客戶端請求里面帶著token來判斷權限,如果驗證成功,就向客戶端返回請求的數據。
5、接口示例
以上分析了這么多,接下來我們就來看一下各大廠定義的接口規范,有哪些值得我們學習的。
銀聯:
支付寶(支付寶需要引入支付寶的sdk,支付寶sdk內部進行了封裝):
招商銀行: