自动跳转登录页和会话重用
为了避免每次打开或刷新应用都要再登录,会话重用是实现短期免登录进入的常用方法。
[任务]
- 打开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);