title |
---|
淘宝天猫的单点登陆如何做到的? |
在开始之前我就在 login.taobao.com
登陆了自己的淘宝账户
之后打开 tmall
看
淘宝将认证 token
存放在 taobao.com
域名下
tmall 想要得到 taobao.com
cookies 必然涉及到跨域
![images] (https://webcdn.chaochaogege.net/images/20190403181308.png)
两个对于 taobao.com
的域名的访问全部使用了 jsonp
第一个 login_api
获取了 taobao.com
域名下的 cookies
以 名为 userCookie
的 JSObject
返回
并回调了 TB.Global.run()
Chrome
对于跨域请求隐藏了 Request Headers
,这里显示 Provisional headers are shown
我当时卡在这里很长时间,因为发现 browser
没有发送 cookies
就收到了 server
回复的 taobao cookies
,肯定遗漏了什么
结果看到 https://www.chromium.org/Home/chromium-security/site-isolation#TOC-Known-Issues
In Chrome's DevTools, cookies and other request headers are not shown in the network panel for cross-site subresource requests. There are also issues with the DOM storage view, security panel, performance panel, and with scrolling over cross-site iframes in mobile device emulation mode, all of which are fixed in Chrome 68.
说是 Chrome 68
就修复了,但我 72 版本还是有这个问题
。先扔这
下面换 Firefox
看请求
TB这个obj绑定了许多方法,可以看下
key的 value 已全部删除
var userCookie = {
dnk: '',
_nk_: '',
_l_g_: '',
ck1: '',
tracknick: '',
mt: '',
l: '',
uc1: '',
t: '',
unb: '',
cna: '',
_tb_token_: '',
version: ''
};
window.TB && TB.Global && TB.Global.run && TB.Global.run();
比较奇怪的是 run
是个空函数,什么都不做,不知道 run
存在的意义是什么
run: function() {}
获取到 usercookie
之后并非什么都不做,事实上整个请求被包裹进了回调,真正的login过程在下面的钩子函数中
这是初始化流程
_initLoginStatus: function() {
var e = o.Global;
var a = o.userInfo;
var t = i.Cookie.get("_nk_") || "";
t = H(unescape(t.replace(/\\u/g, "%u")));
var n = i.Cookie.get("_l_g_") || "";
e._seedLoginApi(function() {
o.userInfo.status = "success";
a.nick = H(unescape(userCookie["_nk_"].replace(/\\u/g, "%u")));
a.displayNick = H(unescape((userCookie["dnk"] || userCookie["_nk_"]).replace(/\\u/g, "%u")));
a.tracknick = H(unescape(userCookie["tracknick"].replace(/\\u/g, "%u")));
a.isLogin = !!(userCookie["_l_g_"] && a.nick);
a.trackId = userCookie["t"] || "";
a.unb = userCookie["unb"] || "";
a.uc1 = userCookie["uc1"] || "";
a.cookie2 = userCookie["cookie2"] || "";
a.tbToken = userCookie["_tb_token_"] || "";
var n = i.unparam(a.uc1);
a.isMallSeller = !!n.tmb;
a.tag = n.tag;
if (a.isLogin && (a.nick != t || !a.tbToken)) {
// 因为已经拿到了usercookie,现在开始
// 跳转到 tmall的域名,然后set cookies
var s = "//tmcc.tmall.com/pass.htm";
if (f) {
s = "//tmcc.daily.tmall.net/pass.htm"
}
A(s)
}
e._fireLoginStatusReadyFnList()
}, function() {
o.userInfo.status = "error";
e._fireLoginStatusReadyFnList()
})
},
_seedLoginApi: function(e, a) {
// 确认 login_api 用哪个接口
var t = "//top-tmm.taobao.com/login_api.do";
if (f) {
t = "//www.daily.taobao.net/go/app/tmall/login-api.php"
}
// e 就是上面的回调function
// success 字面推断成功之后回调e
// 淘宝使用了一个 kissy 的库
// https://docs.kissyui.com/1.1.6/ajax/index.html#method_getScript
t += "?" + Math.random();
i.getScript(t, {
success: e,
error: a,
timeout: 3
})
},
然后到了第二个 jsonp
跨域
_initMemberInfoCallback({
"activeStatus": -1,
"availablePoints": 789,
"cookies": {
"uc1": {
"value": "so many cookies"
},
"t": {
"value": "so many cookies"
},
"unb": {
"value": "so many cookies"
}
},
"expiredPoints": 0,
"lastMessage": "",
"lastMessageId": 0,
"lastMessageType": 0,
"lastMessageUrl": "//vip.tmall.com/messagebox/index.htm",
"login": true,
"mallSeller": false,
"messagePopup": true,
"newMessage": 0,
"newMsgList": {
"1": 0,
"2": 0,
"3": 0
},
"taskId": ""
});
这里回调了新的 _initMemberInfoCallback
函数
m._initMemberInfoCallback = function(t) {
a.memberInfo = t;
a.memberInfo.cookies = {
unb: {
value: a.unb
},
t: {
value: a.t
},
uc1: {
value: a.uc1
},
login: true
};
a.memberInfo.mallSeller = a.isMallSeller;
a.memberInfo.status = "success";
e._fireMemberInfoReadyFnList()
}
接二连三的 302, 最后以 200 作为跳转的结束
通过 跳转到 pass.tmall.com
来给 .tmall.com
域名 设置 cookies
,配置免登陆
最后总结一下, taobao.com
登陆之后将 cookies
放到 taobao.com
然后在 tmail.com
利用跨域向 top-tmm.taobao.com
发 cookies
请求,
后台校验成功之后返回 taobao.com
下的 cookies
,这样我们就在
tmall.com
域名下 得到了 taobao.com
的 cookies
,下面进行多次 302,最后在 pass.tmall.com
下将 这些 cookies
set 上去,由于后台公用鉴权系统,访问 天猫 时我们就已经登陆了
对于 loginout
,其实也差不多,进行多次跳转,每次都将 cookies
发往 阿里的多个域名的 clear
接口