header 頭推薦加上字段:php
用來存放access_token,經過該token對用戶進行認證。能夠理解其做用等同於cookie中的session_id,有該令牌就默認用戶已經認證登錄過。html
用來判斷該請求是不是客戶端APP發起的請求,如今最多見的兩種方法 一種 是經過ASE加密解密來實現。一種是經過比對信息摘要的方式。 最簡單的生成方式是給出一個隨機值和時間戳,還有和服務端約定好的鹽值按照必定的順序進行MD5加密。前端
signture = md5(nonce + timastamp + salt)
複製代碼
服務端收到該簽名的時候,按照約定的順序進行MD5加密比對簽名,若是服務端生成的簽名與客戶端簽名不一致,則可認爲不是客戶端發起的請求,此時不響應該請求。固然使用這種方法要同時提交timestamp 和 隨機字符串nonce。mysql
用來存放客戶端版本號,主要是小版本,大版本的話通常服務端會開新接口。可是比較好的作法,是客戶端每一次發佈都要有一個版本號。就算是這次發佈,服務端沒作任何更改,只要客戶端新發布也應該給一個新的版本號。而後將這個版本號寫入header 頭中。這樣的好處是若是請求出現錯誤,咱們可以定位到是哪一個版本的APP出現了該錯誤,能夠更容易的定位復現錯誤。更詳細地還能夠把設備的操做系統和型號也在header頭提交。android
客戶端發送請求時的時間戳,可用於請求過時判斷。ios
雖然signture的存在,使咱們能夠判斷該請求是不是客戶端生成,從而只響應客戶端的請求。可是這樣安全性仍是不夠。git
一個典型的攻擊手法時,經過抓包客戶端生成的簽名不斷地進行接口請求。最多見的就是利用該簽名不停地請求短信驗證碼接口,直到服務器的驗證碼餘額耗光。web
防止重放攻擊的最有效方式就是保持signture的惟一性。客戶端必須生成惟一的signture,同時服務端也要保證對於一個signture只響應一次請求。redis
客戶端保證每次生成一個惟一地signture的最簡單方式是在生成簽名的方法中加入時間戳。sql
服務端保證對每個signture簽名,只使用一次的實現方法是:對每次請求作判斷,若是該請求攜帶的簽名沒有使用過,則響應該請求,並把該簽名記錄在服務端並標記爲已使用。若是查到該請求攜帶的簽名已經被標記爲已使用,則不響應該請求。
服務端對signture作記錄,通常經過3種方式:
寫進文件的弊端,在於沒法供分佈式系統使用。MySql的弊端在於請求數多時增長了數據庫的壓力。因此最好的方式仍是寫進Redis裏。
咱們保證了客戶端簽名惟一性的方法是將每次請求的signtue記錄在服務端。可是隨着請求的增長,記錄的signture也愈來愈多。每次請求都要逐一比對之前全部請求記錄下的signture,這樣顯然是不合理。
因此咱們正確的方式,不該該是單純把signture寫進文件,mysql,或redis。而應該是作一個文件緩存,或是寫數據庫臨時表,或是redis緩存。
可是若是咱們只比對緩存期內的signture,攻擊者仍是能夠經過使用已到期被緩存清除的signture來進行重放攻擊。因而咱們引入了一個超時機制,若是該請求攜帶的timestamp 比當前的服務端時間相隔已經 大於singture緩存時間,則不響應這個請求。這樣就保證了,攻擊者沒法使用被緩存清除的signture進行重放攻擊。
固然超時時間也不能設置過小,由於客戶端請求到達服務端須要必定的時間。因此超時時間的設置應知足
(服務端當前時間 - 客戶端提交的timestamp) < 超時時間 < signture緩存到期時間
複製代碼
引入超時機制的另外一個好處是防止了一部分的中間人攻擊。由於劫持增長了請求的時間,由於超時機制的存在,可能使被劫持的請求失效。
引入了超時機制之後,可能咱們通常都會這樣寫
if( (服務端當前時間 - 客戶端提交的timestamp) < 超時時間 ){
超時了;
}
複製代碼
可是若是攻擊者 更改了客戶端時間,使客戶端提交的timestamp是一個比服務端時間還超前幾天或是幾年的時間戳,並生成了一個對應的signture。這個時候,當該請求響應後。攻擊者等待一段時間,等緩存中這個signture 失效了,攻擊者就能夠拿着這個signture和timestamp 進行重放攻擊了,由於這個timestamp 超前了服務端時間幾天或幾年,因此
服務端 - 客戶端提交的timestamp = 負數 < 超時時間
複製代碼
因此,經過負數小於超時時間,繞過了超時機制,使signture又能夠從新使用。固然這種狀況下的重放攻擊已經很弱了,由於signture使用過一次就會被緩存,因此經過下溢從新使用signture也要等到上一次signture的緩存失效了。則兩次攻擊之間,便必須隔一段緩存有效期。
因此,更合理的話,除了比對請求時間是否小於超時時間。還應該判斷:
服務端時間 - 客戶端提交的timestamp > 0
複製代碼
引入超時機制的前提是客戶端和服務端的時間偏差在可接受的範圍。
試想一下,若是客戶端請求須要0.5s到達服務端,因此服務端的超時時間設置了1s。可是客戶端的時間比服務端慢了2s 。這個時候當客戶端的timestamp 提交到服務端時 原本應該是
服務端當前時間-客戶端提交的timestamp = 0.5 < 1 // 不超時
複製代碼
結果由於客戶端時間比服務端時間慢了2s,使timestamp 到達服務端時,變成了
服務端當前時間-客戶端提交的timestamp = 2.5s > 1 // 超時
複製代碼
因此客戶端服務端時間不一致的會形成客戶端全部請求都由於超時而沒法響應。
那麼如何保證服務端和客戶端的時間一致性呢? 一個最經常使用的解決方式就是:
服務端給出一個接口返回當前時間戳,
客戶端請求該接口獲取時間戳,加上該請求的響應時間與當前時間戳相減得出時間差。
而客戶端提交的timestamp 就是當前時間戳加上服務端與客戶端的時間差。
複製代碼
加密 仍是 信息摘要 ?
信息摘要的好處在於服務端處理更簡單,只須要生成對應的簽名進行比對便可。
加密的方式生成簽名常見的能夠採用ASE加密,借鑑微信支付寶sdk簽名的生成方式,把header頭的重要的參數,都參與signture的生成。這樣的好處在於更加安全,若是傳輸中header頭的參數被劫持更改,會形成服務端驗籤失敗,則請求天然就不響應了。
timestamp 是要提交10位仍是13位的時間戳呢?
這個問題最多見於用PHP寫的APP後端。由於PHP中使用的時間戳是10位的,time()
也是隻返回一個10位的時間戳。
可是,我仍是認爲應該使用一個13位的時間戳。至少你生成signture的時候應使用13位的時間戳,由於這樣實時性更強,防止客戶端太多的時候,不一樣的客戶端同時生成簽名時,出現signture相同的狀況。
因此,反正客戶端使用的是13位的時間戳,若是提交一個10位的時間戳,它也要進行截取。不如直接提交過來怎麼使用,服務端本身決定的。
<?php
// 13 位時間戳轉10位 進行比對
time() - ceil($timestamp/1000);
// 若是是生成13位時間戳進行比對
list($micro,$time) = expload(' ',microtime());
ceil(($time + $micro)*1000) - $timestamp;
// 更新 其實microtime()是能夠直接返回一個float數據,只須要傳一個常數true
ceil(microtime(true)*1000) - $timestamp
複製代碼
APP開發的其中一個難點就是錯誤定位難,復現難。因此寫日誌就很是重要了。 當前錯誤日誌所需記錄的信息應包含至少如下幾類信息:
APP開發如今比較流行的仍是返回json格式而不是xml格式。
返回json格式的數據通常是這樣的:
{
"status" :200,
"message":"ok",
"data" :{},
}
status 返回請求狀態碼,通常複用http狀態碼。
message 返回請求消息,若是有錯誤這裏寫錯誤信息。
data 是返回的數據
複製代碼
可是我仍是比較喜歡如下這種返回方式
{
"code":0
"data":{}
}
// 爲何請求成功 要使用0做爲code狀態碼呢,0的第一感受不是false嗎?
嗯,錯誤狀況千千萬,而成功只有一種狀況。正數負數千千萬,而0也只有一個。
{
"code":1001
"message":"某個控制器請求出錯"
}
複製代碼
爲何呢?
由於不想用http status來傳達API請求狀態,http status 傳達的是通信層的狀態。API是爲了知足業務,返回的數據應包含業務層的狀態碼。業務層不和通信層耦合,不拿http status 取巧。
固然對於這點,喜歡使用http status 的同窗也有不一樣的見解,這就看我的的喜愛了。
我以爲使用code的好處在於:
雖然app經過接口請求的方式與後端交互,沒有cookie,可是依然可使用session。session的實現不依賴於cookie,若是你把cookie中的session_id 可是打開session的令牌。那麼header頭中的Authorization 字段提交的access_token 一樣能夠當作令牌實現一樣的做用。
由於咱們經過Authorization來獲取認證,因此:
若是你容許同時登錄多臺設備,你只須要登錄後複用user表中的access_token。
若是那你不容許同時登錄多臺設備,則能夠選擇登錄時刷新access_token,這樣就使得其餘在線的設備請求頭中的Authorization字段提交的access_token與user表中的不匹配,天然就被擠下線了。
咱們經過access_token來獲取用戶,也就意味着access_token若是被劫持就等同於用戶的帳戶被盜。
你想一想一樣做爲獲取服務端session的令牌,使用cookie時,爲了安全咱們通常會作哪些呢?
因此,雖然signture的惟一性已經爲咱們證實了是APP發起的合法請求,可是嚴格來講咱們也不能單單對access_token 進行明文傳輸。 咱們能夠考慮在Authorization 字段不是簡單地傳輸access_token的值,能夠傳一個access_token和時間戳的加密字符串,在服務端再進行解密,並先判斷是否超時。若是要安全性高些,還能夠參考signture作惟一性處理。
建議建一個版本升級表用來存放版本升級信息。而且要有是否強制更新字段。
咱們header頭提交version參數,寫日誌爲的都是不想失去對客戶端的控制,能更好的定位錯誤。可是app與傳統的web開發的一個區別,就是web開發頁面作了修改,全部的用戶都能看到修改,可是APP的話,只要用戶沒有更新,已修復的bug,對用戶而言 依舊存在。
版本升級表設計
字段名 | 類型 | 備註 |
---|---|---|
id | int | 主鍵id |
app_type | varchar | 客戶端版本類型 ios or android |
version | int | 開發版本號 |
version_code | varchar | 客戶端版本號(1.0.2) |
upagrade_desc | varchar | 更新提示語 |
apk_url | varchar | 更新包連接 |
is_force | tinyint | 是否強制更新 |
created_at | int | 建立時間 |
status | tinyint | 是否已發佈 |
有了版本升級表之後,咱們就能更方便直觀地管理查看咱們發佈的版本。
並且咱們能夠在打開APP時請求接口,查詢版本設計表得到最新的版本與header頭提交的version字段做對比,判斷是否須要更新,彈出更新窗口。
對於須要強制更新的版本,彈窗應設置爲不容許用戶點擊取消,必定要更新才能使用該APP。這樣咱們就能夠把一些重大更新或者修復一些重要bug的版本設爲強制更新,不更新就不讓繼續使用。
爲了更好地進行用戶分析,咱們還能夠建一個APP登錄記錄表。 打開APP時就經過header把用戶信息記錄起來,用來作用戶分析。用戶日活量,月活量。
客戶端一打開就將數據發給該接口就行,無論請求是否成功,客戶端都不須要關心。
app_active_log 表
字段名 | 類型 | 備註 |
---|---|---|
id | int | 主鍵id |
app_type | varchar | 客戶端版本類型 ios or android |
version | int | 開發版本號 |
version_code | varchar | 客戶端版本號(1.0.2) |
model | varchar | 設備型號 小米 蘋果 |
uid | int | 用戶id |
created_at | int | 建立時間 |
這個表的另外一個功能還能夠統計某個版本的用戶量或是Android仍是IOS用戶多,方便咱們更新版本時選擇先開發IOS版或是安卓版,或者出現bug決定哪一個版本先修復。
常見的APP端異常:
咱們在服務端寫日誌,在header頭提交設備信息這些都是爲了更好地定位客戶端的錯誤。可是咱們的日誌只能記錄接口調用異常。對於客戶端的異常卻無能爲力。
爲此,咱們應該和客戶端配合。把客戶端產生異常按期上報到服務端。方便客戶端工程師定位復現並修復客戶端異常
咱們能夠建一個 app_crap 表來統計收集 crash 卡頓 Exception ANR的次數和影響用戶量 用戶數
字段名 | 類型 | 備註 |
---|---|---|
id | int | 主鍵id |
app_type | varchar | 客戶端版本類型 ios or android |
version | int | 開發版本號 |
version_code | varchar | 客戶端版本號(1.0.2) |
model | varchar | 設備型號 小米 蘋果 |
type | tinyint | 端異常類型 卡頓 閃退 |
description | varchar | 描述 |
created_at | int | 建立時間 |
固然,客戶端記錄這些數據比較麻煩.一個更好的解決方案是在客戶端中集成第三方服務提供的SDK,將這些數據提交到第三方平臺,客戶端工程師能夠登陸第三方平臺查看客戶端異常統計。
經常使用的第三方平臺 :
接口調試神器,發起一個http請求
抓包神器,能夠抓APP發送過來的請求,查看是否有請求提交的參數都是什麼
php一個http 請求包,經過composer 安裝快速使用,可用來寫接口的測試代碼,模擬發起http請求,比起postman的優勢在於,經過代碼實現,自定義更方便。
手機模擬器,能夠在電腦上模擬多個Android系統的手機
內網映射工具。app開發一個麻煩的地方在於沒法本地調試,由於客戶端須要請求有域名或公網ip的服務端代碼,雖然公司有測試服務器,可是有些時候測試服上有不少人同時使用,我git提交了修改後的服務端代碼不能立刻reset hard生效。或者測試服不在我開發的分支。ngrok的好處是內網映射,給你的電腦綁定一個域名。而客戶端測試時填寫這個域名能訪問到你電腦的服務端代碼,實時調試更方便。
以上就是我作APP後端開發的一些總結,因爲是第一次開發APP後端,水平有限,還請你們多多指教