RESTful api接口安全優(yōu)雅設計

RESTful api接口安全優(yōu)雅設計 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??-- 陳萬洲


寫過不少接口,不過一直沒有去總結,網(wǎng)上搜了一下,大同小異,此文根據(jù)以下幾個鏈接整理修改:

https://segmentfault.com/a/1190000004051246

http://blog.sqrtthree.com/2015/09/08/api/

http://keeganlee.me/post/architecture/20160107

https://www.hutuseng.com/article/how-to-design-api

在項目中,需要為APP撰寫API。剛開始接觸的時候,并沒有考慮太多,就想提供URL,APP端通過該URL進行查詢、創(chuàng)建、更新等操作即可。但再對相關規(guī)范進行了解后,才發(fā)現(xiàn),API的設計并沒有那么簡單,遠遠不是URL的問題,而是一個通信協(xié)議的整體架構

1. 使用GET、POST、PUT、DELETE這幾種請求模式

請求模式也可以說是動作、數(shù)據(jù)傳輸方式,通常我們在web中的form有GET、POST兩種,而在HTTP中,存在下發(fā)這幾種。

GET (選擇):從服務器上獲取一個具體的資源或者一個資源列表。?

POST (創(chuàng)建): 在服務器上創(chuàng)建一個新的資源。

PUT(更新):以整體的方式更新服務器上的一個資源。?

PATCH (更新):只更新服務器上一個資源的一個屬性。

DELETE(刪除):刪除服務器上的一個資源。?

HEAD : 獲取一個資源的元數(shù)據(jù),如數(shù)據(jù)的哈希值或最后的更新時間。

OPTIONS:獲取客戶端能對資源做什么操作的信息。

常見的請求參數(shù)

比如在數(shù)據(jù)過多, 需要對數(shù)據(jù)進行分頁請求的時候, 我們應該統(tǒng)一 API 請求參數(shù). 常見的有這些.

limit=10指定返回記錄的數(shù)量

offset=10指定返回記錄的開始位置。

page=2&per_page=100指定第幾頁,以及每頁的記錄數(shù)。

sortby=name&order=asc指定返回結果按照哪個屬性排序,以及排序順序。

animal_type_id=1指定篩選條件

2. 版本

API的開發(fā)直接關系了APP是否可以正常使用,如果原本運行正常的API,突然改動,那么之前使用這個API的APP可能無法正常運行。APP是不可能強迫用戶主動升級的,因此,通過API版本來解決這個問題。也就是說,API的多個版本是同時運行的,而且都要保證可以正常使用。

按照RESTful的規(guī)范,不同的版本也應該用相同的API URL,通過header信息來判斷版本,再調用不同版本的程序進行處理。但是這明顯會給開發(fā)帶來巨大的成本。

解決辦法有以下幾種:

1.新版本兼容舊版本,所有舊版本的動作、字段、操作,都在新版本中可以被實現(xiàn),但明顯這樣的維護成本很大;

2.不同的版本,用不同的URL來提供服務,在URL中通過v1、v2來區(qū)分版本號,比如v2.api.xxx.com/user的方式,或者http://api.domain.com/v2。

3.每個接口有各自的版本,一般為接口添加個version的參數(shù)。

3.json數(shù)據(jù)類型

接口的數(shù)據(jù)一般都采用JSON格式進行傳輸,不過,需要注意的是,JSON的值只有六種數(shù)據(jù)類型:

Number:整數(shù)或浮點數(shù)

String:字符串

Boolean:true 或 false

Array:數(shù)組包含在方括號[]中

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

Null:空類型

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

4.返回的數(shù)據(jù)結構

服務器返回的數(shù)據(jù)結構,一般為:

{code:0,message:"success",data:{key1:value1,key2:value2,...}}

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

message: 描述信息,成功時為"success",錯誤時則是錯誤信息

data: 成功時返回的數(shù)據(jù),類型為對象或數(shù)組

不同錯誤需要定義不同的返回碼,屬于客戶端的錯誤和服務端的錯誤也要區(qū)分,比如1XX表示客戶端的錯誤,2XX表示服務端的錯誤。這里舉幾個例子:

0:成功

100:請求錯誤

101:缺少appKey

102:缺少簽名

103:缺少參數(shù)

200:服務器出錯

201:服務不可用

202:服務器正在重啟

錯誤信息一般有兩種用途:一是客戶端開發(fā)人員調試時看具體是什么錯誤;二是作為App錯誤提示直接展示給用戶看。主要還是作為App錯誤提示,直接展示給用戶看的。所以,大部分都是簡短的提示信息。

data字段只在請求成功時才會有數(shù)據(jù)返回的。數(shù)據(jù)類型限定為對象或數(shù)組,當請求需要的數(shù)據(jù)為單個對象時則傳回對象,當請求需要的數(shù)據(jù)是列表時,則為某個對象的數(shù)組。這里需要注意的就是,不要將data傳入字符串或數(shù)字,即使請求需要的數(shù)據(jù)只有一個,比如token,那返回的data應該為:

//正確data:{token:123456}//錯誤data:123456

5. 使用SSL(https)來提供URL

首先,使用https可以在數(shù)據(jù)包被抓取時多一層加密。我們現(xiàn)在的APP使用環(huán)境大部分都是在路由器WIFI環(huán)境下,一旦路由器被入侵,那么黑客可以非常容易的抓取到用戶通過路由器傳輸?shù)臄?shù)據(jù),如果使用http未經(jīng)加密,那么黑客可以很輕松的獲取用戶的信息,甚至是賬戶信息。

其次,即使使用https,也要在API數(shù)據(jù)傳輸設計時,正確的采用加密。例如直接將token信息放在URL中的做法,即使你使用了https,黑客抓不到你具體傳輸?shù)臄?shù)據(jù),但是可以抓到你請求的URL?。。ú榱速Y料了,https用GET方式請求,也僅能抓到域名字符部分,不能抓到請求的數(shù)據(jù),但是URL可以在瀏覽器或特殊客戶端工具中直接看到。多謝下面的朋友指正錯誤)因此,使用https進行請求時,要采用POST、PUT或者HEAD的方式傳輸必要的數(shù)據(jù)。

6.TOKEN

現(xiàn)在,大部分App的接口都采用RESTful架構,RESTFul最重要的一個設計原則就是,客戶端與服務器的交互在請求之間是無狀態(tài)的,也就是說,當涉及到用戶狀態(tài)時,每次請求都要帶上身份驗證信息。實現(xiàn)上,大部分都采用token的認證方式,一般流程是:

用戶用密碼登錄成功后,服務器返回token給客戶端;

客戶端將token保存在本地,發(fā)起后續(xù)的相關請求時,將token發(fā)回給服務器;

服務器檢查token的有效性,有效則返回數(shù)據(jù),若無效,分兩種情況:

token錯誤,這時需要用戶重新登錄,獲取正確的token

token過期,這時客戶端需要再發(fā)起一次認證請求,獲取新的token

然而,此種驗證方式存在一個安全性問題:當?shù)卿浗涌诒唤俪謺r,黑客就獲取到了用戶密碼和token,后續(xù)則可以對該用戶做任何事情了。用戶只有修改密碼才能奪回控制權。

如何優(yōu)化呢?第一種解決方案是采用HTTPS。HTTPS在HTTP的基礎上添加了SSL安全協(xié)議,自動對數(shù)據(jù)進行了壓縮加密,在一定程序可以防止監(jiān)聽、防止劫持、防止重發(fā),安全性可以提高很多。不過,SSL也不是絕對安全的,也存在被劫持的可能。另外,服務器對HTTPS的配置相對有點復雜,還需要到CA申請證書,而且一般還是收費的。而且,HTTPS效率也比較低。一般,只有安全要求比較高的系統(tǒng)才會采用HTTPS,比如銀行。而大部分對安全要求沒那么高的App還是采用HTTP的方式。

我們目前的做法是給每個接口都添加簽名。給客戶端分配一個密鑰,每次請求接口時,將密鑰和所有參數(shù)組合成源串,根據(jù)簽名算法生成簽名值,發(fā)送請求時將簽名一起發(fā)送給服務器驗證。類似的實現(xiàn)可參考OAuth1.0的簽名算法。這樣,黑客不知道密鑰,不知道簽名算法,就算攔截到登錄接口,后續(xù)請求也無法成功操作。不過,因為簽名算法比較麻煩,而且容易出錯,只適合對內的接口。如果你們的接口屬于開放的API,則不太適合這種簽名認證的方式了,建議還是使用OAuth2.0的認證機制。

我們也給每個端分配一個appKey,比如Android、iOS、微信三端,每個端分別分配一個appKey和一個密鑰。沒有傳appKey的請求將報錯,傳錯了appKey的請求也將報錯。這樣,安全性方面又加多了一層防御,同時也方便對不同端做一些不同的處理策略。

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

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

用戶不再需要記住密碼了,也不怕密碼泄露的問題了;

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

7.用戶設計

顯式用戶和隱式用戶,我不知道這兩個詞用的是否確切。?

顯式用戶指的是,APP程序中有用戶系統(tǒng),一個username、password正確的合法用戶,稱之為顯式的用戶,

通常顯式用戶都需要注冊,登錄以后能完成一些個人相關的操作。

隱式用戶指的是,APP程序本身就沒有用戶系統(tǒng),或者一個在沒有登錄的情況下,使用我們APP的用戶。

在這種情況下,可以通過客戶端生成的UDID來標識一個用戶。

有了用戶信息,我們就能夠了解不同用戶的使用習慣,而不僅僅是全體用戶的一個整體的統(tǒng)計信息,

有了這些個體的信息之后,就可以做一些用戶分群、個性化推薦之類的事情。

如果是SAAS版本,還需要區(qū)分不同商戶的用戶

8.良好的接口說明文檔和測試程序

接口文檔有時候是項目初期就定下來的,前后端開發(fā)人員按照接口規(guī)范開發(fā),

有的是接口開發(fā)完成后寫的。

接口文檔要清晰、明了,包含多少個接口,每個接口的地址、參數(shù)、請求方式、數(shù)據(jù)交換格式、返回值等都要寫清楚。

接口測試程序,有條件的話,也可以提供,方便前后端的調試。

如果是springMVC開發(fā)的話,可以用swagger,后端只要加幾個注釋發(fā)一個url給前端就可以了,輕松又高效。

9.接口統(tǒng)計功能

在做PC端網(wǎng)站的時候,我們都會給我們的網(wǎng)站加上個統(tǒng)計功能,要么自己寫統(tǒng)計系統(tǒng),要么使用第三方的比如GA、百度等。

移動端接口API則需要我們自己實現(xiàn)統(tǒng)計功能,

這時就需要我們盡可能多的收集客戶端的信息,除了傳統(tǒng)的IP、User-Agent之外,還應該收集一些移動相關的信息,

比如

手機操作系統(tǒng),是android還是ios,都是什么版本,

用戶使用的網(wǎng)絡狀況,是2G、3G、4G還是WIFI

客戶端APP是什么版本信息。

這樣,有助于我們更好的了解我們用戶的使用情況。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容