進入與退出應用 / 會話重用與自動登入

天笑發表於2017-02-21

自動跳轉登入頁和會話重用

為了避免每次開啟或重新整理應用都要再登入,會話重用是實現短期免登入進入的常用方法。

[任務]

  • 開啟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.tryAutoLoginMUI.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);

相關文章