筆記:如何獲取網站根域名

首先聲明,這裏所說的「根域名」,並非指「全球共有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

PSL

接着上 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。在一些須要跨域的場景中,可能會見到這貨的身影。好比這篇文章 所描述的,「相同主域名不一樣子域名下的頁面,能夠設置 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下最無敵的跨大域方案》

功虧一簣。心好累啊。

Cookie 救火

最後想起前面提到的一句,「想在一個叫 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;
    }
  }
};
相關文章
相關標籤/搜索