变脸式应用 / 会话重用与自动登录

自动跳转登录页和会话重用

为了避免每次打开或刷新应用都要再登录,会话重用是实现短期免登录进入的常用方法。

[任务]

  • 打开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);
发布了65 篇原创文章 · 获赞 16 · 访问量 8万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章