設計基於HTML5的APP登錄功能及安全調用接口的方式(原理篇)
最近發現羣內大夥對用Hbuilder做的APP怎麼做登錄功能以及維護登錄狀態非常困惑,而我前一段時間正好稍微研究了一下,所以把我知道的告訴大家,節約大家查找資料的時間。
你是否真的需要登錄功能?
把這個問題放在最前面並不是灌水,而是真的見過很多並不需要登錄的APP去做了登錄功能,或者是並不需要強制登錄的APP把登錄作爲啓動頁。
用戶對你的APP一無所知,你就要求對方註冊並登錄,除非APP本身已經很有名氣或者是用戶有強需求,否則正常人應該會直接把它刪掉。
比較溫和的方式是將一些並不需要登錄,但可以給用戶帶來幫助的東西,第一時間展現給他們,讓他們產生興趣,再在合適的時機引導他們註冊(比如使用需要使用更高級的功能,或用戶需要收藏某個喜歡的信息時)。
登錄和註冊要足夠簡單
這是小小的手機端,用再好的輸入法,打字也是不方便的,所以別把登錄頁設計得需要填很多東西。如果有可能的話,只填手機號,讓用戶收到短信驗證碼就完成註冊是最好不過的了。想獲得更多信息?想想大公司的APP是怎麼做的,他們會告訴用戶,現在的個人資料完善程度是30%,如果想獲得更多積分,你需要填完。
tips:如果你想發佈在Appstore並且同時包含註冊功能,那麼註冊頁面必須做一個用戶許可協議的鏈接,否則有可能通不過審覈。
實現登錄後的session有幾種方式?
APP當瀏覽器用,直接載入遠程頁面
這種情況是很多偷懶的程序員或者傻X的老闆選擇的方式,因爲做起來實在太快。如果本身網站是響應式佈局,那麼很有可能不需要做什麼更改,就只要在開發時打開首頁就好了,這樣Hybird的APP外殼就純粹成爲了一個瀏覽器。
但比起這樣做帶來的無數缺點來,開發速度快的優點幾乎可以忽略不計。
首先,在網絡環境不佳時,純大白頁,用戶體驗0;
然後,CSS和JS等資源不在本地,需要遠程載入,如果使用了bootstrap之類的框架,那用戶爲了開一下APP而耗費的流量真是令人感動;
再然後,網頁裏常用的jquery,在手機的webview裏速度並不理想,而如果是非ajax的網頁那就更糟心了,每次操作都要跳轉和頁面渲染,要讓人把它當成APP那實在是笑話。
再再然後,這樣的所謂APP,要通過Appstore的審查,那是做夢的(除非審覈員當天鬧肚子嚴重,拿着紙巾奔向廁所前誤點了通過……),蘋果的要求是,這得是APP,而不能是某個網站做成APP的樣子,那樣的情況適合做Web APP。而據我所知,國內幾個較大的Android市場,這樣的APP也是無法通過審覈的。
調用後端接口
這是個很好的時代,因爲無論後端你是用Java、PHP,還是node.js,都可以通過xml、json來和APP通訊。遙想當年寫服務端要自己寫包結構,然後爲了解決併發問題還折騰了半年IOCP模型,真心覺得現在太幸福了。
把剛纔那個用APP當瀏覽器使的案例的所有缺點反過來看,就是這樣做的優點,在優化完善的情況下體驗接近原生,而且通訊流量極少,通過各種審覈也是妥妥的。
tips:通過plus對象中的XMLHttpRequest來Get、Post遠程的後端接口,或者使用Mui中封裝好的AJAX相關函數。
插一段代碼,我把mui的ajax又做了進一步的封裝,對超時進行了自動重試,而對invalid_token等情況也做相應處理:
調用後端接口怎麼樣才安全?
在APP中保存登錄數據,每次調用接口時傳輸
程序員總能給自己找到偷懶的方法,有的程序爲了省事,會在用戶登錄後,直接把用戶名和密碼保存在本地,然後每次調用後端接口時作爲參數傳遞。真省事兒啊!可這種方法簡單就像拿着一袋子錢在路上邊走邊喊“快來搶我呀!快來搶我呀!”,一個小小的嗅探器就能把用戶的密碼拿到手,如果用戶習慣在所有地方用一個密碼,那麼你闖大禍了,黑客通過撞庫的方法能把用戶的所有信息一鍋端。
登錄時請求一次token,之後用token調用接口
這是比較安全的方式,用戶在登錄時,APP調用獲取token的接口(比如http://api.abc.com/get_token/),用post將用戶名和密碼的摘要傳遞給服務器,然後服務器比對數據庫中的用戶信息,匹配則返回綁定該用戶的token(這一般翻譯爲令牌,很直觀的名字,一看就知道是有了這玩意,就會對你放行),而數據庫中,在用戶的token表中也同時插入了這個token相關的數據:這個token屬於誰?這個token的有效期是多久?這個token當前登錄的ip地址是?這個token對應的deviceid是?……
這樣即便token被有心人截獲,也不會造成太大的安全風險。因爲沒有用戶名和密碼,然後如果黑客通過這個token僞造用戶請求,我們在服務器端接口被調用時就可以對發起請求的ip地址、user-agent之類的信息作比對,以防止僞造。再然後,如果token的有效期設得小,過一會兒它就過期了,除非黑客可以持續截獲你的token,否則他只能乾瞪眼。(插一句題外話:看到這裏,是不是明白爲什麼不推薦在外面隨便接入來歷不明的wifi熱點了?)
tips:token如何生成? 可以根據用戶的信息及一些隨機信息(比如時間戳)再通過hash編碼(比如md5、sha1等)生成唯一的編碼。
tips:token的安全級別,取決於你的實際需求,所以如果不是涉及財產安全的領域,並不建議太嚴格(比如用戶走着走着,3G換了個基站,閃斷了一下IP地址變了,尼瑪token過期了,這就屬於爲了不必要的安全丟了用戶體驗,當然如果變換的IP地址跨省的話還是應該驗證一下的,想想QQ有時候會讓填驗證碼就明白了)。
tips:接口在返回信息時,可以包含本次請求的狀態,比如成功調用,那麼result['status']可能就是'success',而反之則是'error',而如果是'error',則result['errcode']中就可以包含錯誤的原因,比如errcode中是'invalid_token'就可以告訴APP這個token過期或無效,這時APP應彈出登錄框或者用本地存儲的用戶名或密碼再次請求token(用戶選擇“記住密碼”,就應該在本地保存用戶名和密碼的摘要,方法見plus.storage的文檔)。
再插點代碼,基於plus.storage的用戶信息類,注意:需要在plusReady之後再使用。
這樣當用戶啓動APP或使用了需要登錄才能使用的功能時,就可以使用UserInfo.has_login()來判斷是否已經登錄,如果已登錄,則使用UserInfo.token()來獲取到token數據,作爲參數調用遠程的後端接口。
在登錄頁面中,用戶輸入了用戶名和密碼後,並點擊了”登錄“按鈕,我們下一步做什麼?再插段代碼(注意:此處使用的是我剛纔代碼中擴展的web_query函數,你也可以直接使用mui的ajax):
更安全一點,獲取token通過SSL
剛纔的方法,機智一點兒的讀者大概會心存疑慮:那獲取token時不還是得明文傳輸一次密碼嗎?
是的,你可以將這個獲取token的地址,用SSL來保護(比如https://api.abc.com/get_token/),這樣黑客即使截了包,一時半會兒也解不出什麼信息。
SSL證書的獲取渠道很多,我相信你總有辦法查到,所以不廢話了。不過話說namecheap上的SSL證書比godaddy的要便宜得多……(這是吐槽)
tips:前段時間OpenSSL漏洞讓很多服務器遭殃,所以如果自己搭服務器,一定記得裝補丁。
tips:可以把所有接口都弄成SSL的嗎?可以。但會拖慢服務器,如果是配置並不自信的VPS,建議不折騰。
還要更更安全(這標題真省事)
還記得剛纔APP向服務器請求token時,可以加入的用戶信息嗎?比如用戶的設備deviceid。
如果我們在調用接口時,還附帶一個當前時間戳參數timestamp,同時,用deviceid和這個時間戳再生成一個參數sign,比如 md5(deviceid timestamp token)這樣的形式。而服務端首先驗證一下參數中的時間戳與當前服務器時間是否一致(誤差保持在合理範圍內即可,比如5分鐘),然後根據用戶保存在服務器中的deviceid來對參數中的時間戳進行相同的變形,驗證是否匹配,那便自然“更更安全”了。
tips:如果對整個調用請求中的參數進行排序,再以deviceid和timestamp加上排序後的參數來對整個調用生成1個sign,黑客即使截獲sign,不同的時間點、參數請求所使用的sign也是不同的,難以僞造,自然會更安全。當然,寫起來也更費事。
tips:明白了原理,整個驗證過程是可以根據自己的需求改造的。