天天看點

App架構設計經驗談:接口”安全機制”的設計App架構設計經驗談:接口”安全機制”的設計

App與伺服器的通信接口如何設計得好,需要考慮的地方挺多的,在此根據我的一些經驗做一些總結分享,旨在抛磚引玉。

現在,大部分App的接口都采用RESTful架構,RESTFul最重要的一個設計原則就是,用戶端與伺服器的互動在請求之間是無狀态的,也就是說,當涉及到使用者狀态時,每次請求都要帶上身份驗證資訊。實作上,大部分都采用token的認證方式,一般流程是:

使用者用密碼登入成功後,伺服器傳回token給用戶端;

用戶端将token儲存在本地,發起後續的相關請求時,将token發回給伺服器;

伺服器檢查token的有效性,有效則傳回資料,若無效,分兩種情況:

token錯誤,這時需要使用者重新登入,擷取正确的token

token過期,這時用戶端需要再發起一次認證請求,擷取新的token

然而,此種驗證方式存在一個安全性問題:當登入接口被劫持時,黑客就擷取到了使用者密碼和token,後續則可以對該使用者做任何事情了。使用者隻有修改密碼才能奪回控制權。

如何優化呢?第一種解決方案是采用HTTPS。HTTPS在HTTP的基礎上添加了SSL安全協定,自動對資料進行了壓縮加密,在一定程式可以防止監聽、防止劫持、防止重發,安全性可以提高很多。不過,SSL也不是絕對安全的,也存在被劫持的可能。另外,伺服器對HTTPS的配置相對有點複雜,還需要到CA申請證書,而且一般還是收費的。而且,HTTPS效率也比較低。一般,隻有安全要求比較高的系統才會采用HTTPS,比如銀行。而大部分對安全要求沒那麼高的App還是采用HTTP的方式。

我們目前的做法是給每個接口都添加簽名。給用戶端配置設定一個密鑰,每次請求接口時,将密鑰和所有參數組合成源串,根據簽名算法生成簽名值,發送請求時将簽名一起發送給伺服器驗證。類似的實作可參考OAuth1.0的簽名算法。這樣,黑客不知道密鑰,不知道簽名算法,就算攔截到登入接口,後續請求也無法成功操作。不過,因為簽名算法比較麻煩,而且容易出錯,隻适合對内的接口。如果你們的接口屬于開放的API,則不太适合這種簽名認證的方式了,建議還是使用OAuth2.0的認證機制。

我們也給每個端配置設定一個appKey,比如Android、iOS、微信三端,每個端分别配置設定一個appKey和一個密鑰。沒有傳appKey的請求将報錯,傳錯了appKey的請求也将報錯。這樣,安全性方面又加多了一層防禦,同時也友善對不同端做一些不同的處理政策。

另外,現在越來越多App取消了密碼登入,而采用手機号+短信驗證碼的登入方式,我在目前的項目中也采用了這種登入方式。這種登入方式有幾種好處:

不需要注冊,不需要修改密碼,也不需要因為忘記密碼而重置密碼的操作了;

使用者不再需要記住密碼了,也不怕密碼洩露的問題了;

相對于密碼登入其安全性明顯提高了。

接口的資料一般都采用JSON格式進行傳輸,不過,需要注意的是,JSON的值隻有六種資料類型:

Number:整數或浮點數

String:字元串

Boolean:true 或 false

Array:數組包含在方括号[]中

Object:對象包含在大括号{}中

Null:空類型

是以,傳輸的資料類型不能超過這六種資料類型。以前,我們曾經試過傳輸Date類型,它會轉為類似于"2016年1月7日 09時17分42秒 GMT+08:00"這樣的字元串,這在轉換時會産生問題,不同的解析庫解析方式可能不同,有的可能會轉亂,有的可能直接異常了。要避免出錯,必須做特殊處理,自己手動去做解析。為了根除這種問題,最好的解決方案是用毫秒數表示日期。

另外,以前的項目中還出現過字元串的"true"和"false",或者字元串的數字,甚至還出現過字元串的"null",導緻解析錯誤,尤其是"null",導緻App奔潰,後來查了好久才查出來是該問題導緻的。這都是因為服務端對資料沒處理好,導緻有些資料轉為了字元串。是以,在用戶端,也不能完全信任服務端傳回的資料都是對的,需要對所有異常情況都做相應處理。

伺服器傳回的資料結構,一般為:

code: 傳回碼,0表示成功,非0表示各種不同的錯誤

message: 描述資訊,成功時為"success",錯誤時則是錯誤資訊

data: 成功時傳回的資料,類型為對象或數組

不同錯誤需要定義不同的傳回碼,屬于用戶端的錯誤和服務端的錯誤也要區分,比如1XX表示用戶端的錯誤,2XX表示服務端的錯誤。這裡舉幾個例子:

0:成功

100:請求錯誤

101:缺少appKey

102:缺少簽名

103:缺少參數

200:伺服器出錯

201:服務不可用

202:伺服器正在重新開機

錯誤資訊一般有兩種用途:一是用戶端開發人員調試時看具體是什麼錯誤;二是作為App錯誤提示直接展示給使用者看。主要還是作為App錯誤提示,直接展示給使用者看的。是以,大部分都是簡短的提示資訊。

data字段隻在請求成功時才會有資料傳回的。資料類型限定為對象或數組,當請求需要的資料為單個對象時則傳回對象,當請求需要的資料是清單時,則為某個對象的數組。這裡需要注意的就是,不要将data傳入字元串或數字,即使請求需要的資料隻有一個,比如token,那傳回的data應該為:

接口不可能一成不變,在不停疊代中,總會發生變化。接口的變化一般會有幾種:

資料的變化,比如增加了舊版本不支援的資料類型

參數的變化,比如新增了參數

接口的廢棄,不再使用該接口了

為了适應這些變化,必須得做接口版本的設計。實作上,一般有兩種做法:

每個接口有各自的版本,一般為接口添加個version的參數。

整個接口系統有統一的版本,一般在URL中添加版本号,比如http://api.domain.com/v2。

大部分情況下會采用第一種方式,當某一個接口有變動時,在這個接口上疊加版本号,并相容舊版本。App的新版本開發傳參時則将傳入新版本的version。

如果整個接口系統的根基都發生變動的話,比如微網誌API,從OAuth1.0更新到OAuth2.0,整個API都進行了更新。

有時候,一個接口的變動還會影響到其他接口,但做的時候不一定能發現。是以,最好還要有一套完善的測試機制保證每次接口變更都能測試到所有相關層面。

關于接口設計,暫時想到的就這麼多了。各位看官看完覺得有遺漏或有哪些需要優化的歡迎提出一起讨論。

繼續閱讀