首先聲明,這裏所說的「根域名」,並非指「全球共有13臺根邏輯域名服務器」這句話中的「根域名」。而是指某一個站點的「根域名」。javascript
百度搜索是「www.baidu.com」,百度翻譯的域名是「fanyi.baidu.com」,百度地圖的域名則是「map.baidu.com」。這些域名有共同的部分「baidu.com」。在本文中,咱們將「baidu.com」這樣的域名稱爲「根域名」。前端同窗應該都知道,在「.baidu.com」這一域下的 cookie 能夠在其餘子站點下拿到(固然,前提是端口號和協議都保持一致)。html
最近開發的過程當中趕上了一個小問題。不管訪問哪一個子站點,都要經過 js 將 cookie 存放到根域名下。前端
一開始比較大意,直接拿正則匹配。問題是忽略了這世界上還存在「www.xxx.edu.cn」這樣的站點。在這種狀況下,顯然咱們不能認爲」edu.cn「是根域名。想在一個叫「edu.cn」的域下存 cookie?對不起,瀏覽器作不到。(這句話很重要。)java
正則匹配是作不到了。搜索了一下,網上也沒有什麼特別好的解決方案。無非是枚舉出國內常見的一些頂級域名,而後再進行處理,以下面這個 PHP 的例子:git
但如何確保咱們枚舉出的例子必定是徹底的無遺漏的呢?不完美,放棄。github
接着上 github 上去找例子。卻是發現了一些解決域名的工具。好比一個名爲 psl 的倉庫。web
PSL 是 「Public Suffix List」 的縮寫,這個「公共域名後綴列表」項目原本是供瀏覽器廠商使用的。能夠訪問官網,另外建議看看這篇《域名小知識:Public Suffix List》。正則表達式
我搜索到的這個 psl 倉庫正是基於 PSL、使用 js 來解析域名的。粗略看了下,存放域名的 json 文件有 108 KB。嚇死了。json
另外一款叫作 parse-domain 的,光是生成的正則表達式文件就有 203 KB。跨域
沒辦法,一個跑到瀏覽器上的前端腳本,自己不到 1500 行,爲了一個判斷引入上百 KB 的外部依賴,實在不划算。
因而只能本身另起爐竈,想一想別的辦法。
首先想到的是 document.domain
。在一些須要跨域的場景中,可能會見到這貨的身影。好比這篇文章 所描述的,「相同主域名不一樣子域名下的頁面,能夠設置 document.domain 讓它們同域」。
通過測試發現,對於域名c.example.edu.cn
下的頁面,能夠執行下面這句:
document.domain = 'example.edu.cn';
而在 Chrome 下,下面這句則沒法執行:
// DOMException document.domain = 'edu.cn';
瀏覽器會拋出DOMException
:
1 Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'edu.cn' is not a suffix of 'c.example.edu.cn'.
IE 也會報出「參數無效」的錯誤;Firefox 下一樣會拋出錯誤:
NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN: Illegal document.domain value
從報錯信息能夠看到, DOMException 是能夠捕獲到的。那就好辦了。
將域名(或頁面當前的 document.domain
) split 成數據 ['x', 'example', 'edu', 'cn']
,從右向左逐次加上一個元素,每次將單詞使用句點鏈接並賦值給 document.domain
。若是 catch 到錯誤,則進行下一次操做。一旦賦值成功,便可 break 循環。
上代碼:
const domain = document.domain; const list = domain.split('.'); let len = list.length; let rootDomain = domain; while (len--) { try { document.domain = list.slice(len).join('.'); rootDomain = document.domain; break; } catch (e) {} } // 還得恢復原值,避免產生反作用 document.domain = domain; console.log(rootDomain);
很好,通過簡單測試,Chrome、IE 妥妥的。
然而 Firefox 掛了。掛在最後的還原階段。也就是說,Firefox 容許修改 document.domain
,但不容許修改爲上一級以後,再回退到下一級。
此外(感謝老大),在 Safari 上測試發現,document.domain = 'cn'
不會報錯!什麼鬼?請移步《Webkit下最無敵的跨大域方案》。
功虧一簣。心好累啊。
最後想起前面提到的一句,「想在一個叫 edu.cn 的域下存 cookie?對不起,瀏覽器作不到。」
要不咱試試 cookie?動手!
道理同上,每次嘗試在手動拼接的 domain 下面存 cookie,而後檢查 cookie 是否保存成功。一旦成功,則 break 循環,並清除該 cookie。沒有反作用,只是多操做幾回 cookie。
換個思路,總算是解決了。
代碼被我放在了 Github 上。順手貼在這裏:
var KEY = '__rT_dM__' + (+new Date()); var R = new RegExp('(^|;)\\s*' + KEY + '=1'); var Y1970 = (new Date(0)).toUTCString(); module.exports = function getRootDomain() { var domain = document.domain || location.hostname; var list = domain.split('.'); var len = list.length; var temp = ''; var temp2 = ''; while (len--) { temp = list.slice(len).join('.'); temp2 = KEY + '=1;domain=.' + temp; // try to set cookie document.cookie = temp2; if (R.test(document.cookie)) { // clear document.cookie = temp2 + ';expires=' + Y1970; return temp; } } };