自動跳轉登錄頁和會話重用
爲了避免每次打開或刷新應用都要再登錄,會話重用是實現短期免登錄進入的常用方法。
[任務]
- 打開H5應用時(從任意入口頁進入),如果剛剛登錄過,則可以免登錄直接打開入口頁。
如果尚未登錄過,則跳轉至登錄頁面,待登錄成功後跳轉到入口頁。 - 點退出登錄回到首頁,如果首頁必須登錄,則到登錄頁。
- 在操作過程中,一旦調用某個接口返回了未登錄錯誤(實際中可能是客戶端掉線、服務端重啓等情況),應自動跳轉到登錄頁。
要實現這樣的需求,需要有以下接口設計:
- 登錄接口
login
,登錄成功返回用戶信息,例如login(uname, pwd) -> [0, {用戶信息如id, name, ...}]
.
登錄接口可以支持多種方式登錄,如用戶名/密碼,手機號/動態驗證碼,以及下一節要講述的自動登錄token等。 - 其它所有要求用戶登錄後才能調用的接口,在未登錄時返回統一的錯誤碼:
fn1() -> [2, "未登錄"]
(筋斗雲中未登錄錯誤碼爲2) - 退出接口
logout() -> [0, "OK"]
. - 有一個特別的檢測是否需要登錄的接口,如果已登錄,返回與
login
接口相同的信息,例如接口User.get() -> [0, {用戶信息如id, name, ...}]
.
這個特別的檢測登錄接口的主要用途是,通過會話重用(session),實現短時間內免登錄。
會話重用一般由服務端設置cookie實現,由於瀏覽器會自動記住cookie,只要服務端會話未過期且用戶未退出(logout),就可以一直免登錄進入。
即使不使用cookie而用URL參數(比如token)的,H5應用只要自行記住這個token到本地存儲,下次打開時重用即可。
這樣,前端進入H5應用時的邏輯就是:
- 調用檢測用戶登錄的接口
User.get
,調用成功後將返回信息存儲到全局變量g_data.userInfo
中,並顯示入口頁; - 如果調用失敗,則顯示登錄頁;
- 在登錄成功後,跳轉回一開始要進入的入口頁。
這些邏輯由框架函數MUI.tryAutoLogin
和MUI.handleLogin
提供,應注意把這段邏輯放置到muiInit
事件中,以便在顯示任意頁面之前調用。
我們在筋斗雲示例應用中,可以看到這樣的代碼:(index.js中)
$(document).on("muiInit", myInit);
function myInit()
{
MUI.tryAutoLogin(handleLogin, "User.get");
}
function handleLogin(data)
{
MUI.handleLogin(data);
// g_data.userInfo已賦值
}
在MUI.tryAutoLogin中指定了檢查會話重用的接口名爲”User.get”,於是H5應用便有了會話重用的功能。
在模擬接口中,我們看到”User.get”接口模擬如下:
"User.get": [0, user],
這表明模擬的是已登錄過的狀態,因此打開應用時可直接免登錄進入。
我們把它改成返回”未登錄”錯誤:
"User.get": [2, "no auth"],
這時刷新H5應用,是不是進入了登錄頁?
任意輸入手機號和驗證碼可登錄進來。進入頁面”我”,點退出,看看是不是回到了登錄頁?
注意框架定義了“未認證錯誤”缺省錯誤碼爲2,如果要修改,可以用:
window.E_NOAUTH = 2;
再看會話中斷時的行爲,由於進入訂單列表頁會調用接口”Ordr.query”,我們在瀏覽器控制檯上修改模擬接口讓它返回未登錄錯:
MUI.mockData["Ordr.query"] = [2, "no auth"];
進入訂單列表頁(如果之前已經打開過,可以下拉刷新下),看看是不是自動跳轉到登錄頁了?
如果後端接口格式不是使用筋斗雲調用規範,則需要按上節介紹自行適配接口,在其中添加自動跳轉登錄頁的的邏輯,如:
MUI.callSvrExt['default'] = {
...
dataFilter: function (data) {
...
if (data.code == E_NOAUTH) {
MUI.showLogin();
return;
}
}
};
這樣,框架可以確保未登錄時(或已掉線、服務端重啓等情況時)調用了後端需要登錄的接口,可以自動跳轉到登錄頁。
注意:調用MUI.showLogin()
來顯示登錄頁,而不要用MUI.showPage("#login")
來寫死頁面,而且MUI.showLogin
可以在登錄成功後跳回登錄前想進入的頁面。
類似的還有MUI.showHome()
來顯示首頁。
上面示例中,用MUI.tryAutoLogin
要求進入應用必須先登錄。如果某些入口頁可以免登錄直接進入,則應這樣調用:
function myInit()
{
MUI.tryAutoLogin(handleLogin, "User.get", true); // 參數true表示允許未登錄進入
}
這時應特別小心,可用g_data.userInfo == null
判斷是否爲未登錄。
從未登錄的入口頁進入其它需要登錄才能展示的頁面,也常常在pagebeforeshow
事件中添加判斷:
function onPageBeforeShow(ev, opt)
{
// 可能從一個未登錄的頁面跳轉過來
if (g_data.userInfo == null) {
MUI.showLogin();
return;
}
// 設置頁面內容
}
注意:在MUI.tryAutoLogin中調用接口時,都使用的是同步調用且忽略錯誤。
自動登錄
自動登錄是一個常見需求,基本上現在的手機應用,登錄過一次後,下次都是免登錄進入。
前面已經講過通過會話重用,可以實現短時間內免登錄。
通過對cookie設置較長的超時時間,且在後端長期保存會話數據,可以延長免登錄的時間。
如果會話重用機制的實現並不可靠,比如過期、後端過載或重啓等導致會話丟失,最好再設計專門的自動登錄機制。
要實現自動登錄,客戶端必須將登錄信息保存在本地。由於用戶名、密碼這些信息很敏感,不適合直接存儲在客戶端,一般通過token來實現自動登錄。
需要後端login接口支持token,注意token參數要求通過POST參數傳遞的:
login(uname, pwd) -> {_token, ...} // 普通登錄,額外返回_token字段
login()(token) -> {_token?, ...} // 可以不再返回token
與之前的login
接口相比,普通的登錄方式可返回一個_token
字段,將這個字段保存在客戶端本地,下次就可以通過login(token)
方式自動登錄。
服務器在實現時,一般在token中包含了用戶信息,token過期時間等信息,當然進行了加密,所以比較安全。
H5應用要實現的邏輯如下:
- 進入應用時,先嚐試會話重用,在會話重用失敗後,再嘗試自動登錄
- 如果在操作過程中用戶掉線(如客戶端長時間未操作導致會話超時),也可通過自動登錄,對用戶透明地實現恢復登錄後繼續操作。
框架已經在MUI.tryAutoLogin
函數及默認的後端接口適配中完成以上邏輯,只要服務端接口符合上面約定,無需額外代碼。
我們來模擬接口,讓User.get接口返回未登錄,讓login接口支持返回_token,看看H5應用的行爲:
"login": function (param, postParam) {
if (postParam.token) {
console.log("用token自動登錄");
return [0, user];
}
return [0, $.extend({_token: "abcdefg"}, user)];
},
...
"User.get": [2, "no auth"],
- 刷新H5應用,因爲未登錄過,正確進入登錄頁,注意看瀏覽器控制檯的日誌,只調用了”User.get”接口,失敗後轉到登錄頁。
- 在成功登錄一次後,再次刷新H5應用,發現可以直接進入應用了,看日誌,先調用了”User.get”失敗,然後嘗試自動登錄調用了”login”接口成功。
- 直到去頁面“我”點擊“退出”,刷新H5應用纔不再自動登錄。
如果是自行適配接口,只需將前面示例中跳轉登錄頁的操作換成嘗試自動登錄,示例如下:
MUI.callSvrExt['default'] = {
...
dataFilter: function (data) {
...
if (data.code == E_NOAUTH) {
// 嘗試自動登錄,如果登錄成功則重新發起當前請求;登錄失敗會自動轉向登錄頁
if (MUI.tryAutoLogin()) {
$.ajax(this);
}
// MUI.showLogin();
return;
}
}
};
注意:
上述對會話重用和自動登錄的支持,核心是進入應用時及應用掉線時調用MUI.tryAutoLogin
函數,而它是基於筋斗雲後端的接口設計。
如果後端接口設計不同,可自行來寫一個tryAutoLogin
函數,在進入應用時及應用掉線時調用。
特別地,在tryAutoLogin中調用接口,一般使用同步調用(選項{async: false}
),且忽略出錯(選項{noex:1}
):
var opt = {async: false, noex: 1};
callSvr("User.get", $.noop, null, opt);