Skip to content

Instantly share code, notes, and snippets.

@taikulawo
Last active April 4, 2019 06:35
Show Gist options
  • Save taikulawo/495a8d8631930cdbf6895f236b4190ac to your computer and use it in GitHub Desktop.
Save taikulawo/495a8d8631930cdbf6895f236b4190ac to your computer and use it in GitHub Desktop.
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 以 名为 userCookieJSObject 返回 并回调了 TB.Global.run()


Chrome 对于跨域请求隐藏了 Request Headers,这里显示 Provisional headers are shown

但同一个请求,在 Firefox 上可以完全显示

我当时卡在这里很长时间,因为发现 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.comcookies 请求, 后台校验成功之后返回 taobao.com 下的 cookies ,这样我们就在

tmall.com 域名下 得到了 taobao.comcookies,下面进行多次 302,最后在 pass.tmall.com 下将 这些 cookies set 上去,由于后台公用鉴权系统,访问 天猫 时我们就已经登陆了

对于 loginout ,其实也差不多,进行多次跳转,每次都将 cookies 发往 阿里的多个域名的 clear 接口

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment