如何使用Greasemonkey从远程域获取cookie?

时间:2014-11-16 01:01:11

标签: javascript cookies greasemonkey gm-xmlhttprequest

我正在编写一个Greasemonkey(v2.3)脚本,该脚本基本上屏蔽了由lema.rae.es/drae/srv/search提供的内容,因为缺少任何类型的API。

问题是,我想从Google Translate查询该网址,这是一个不同的域名。为此,我可以毫无问题地使用GM_xmlhttpRequest,但是对特定URL(例如lema.rae.es/drae/srv/search?val=test)的GET请求会产生一个HTML页面,其中隐藏的表单在调用challenge() javascript函数后获得POST - 它会计算在POST请求中传递的某种令牌。

显然,这是异步发生的,Greasemonkey没有看到它。通过反复试验我已经意识到,如果我的浏览器(Iceweasel 31.2.0)有一个lema.drae.es的cookie,那么使用GM_xmlhttpRequest发出的GET请求实际上会返回我想要的,这就是HTML作为参数传递的单词的定义" val"在URL中。但是,如果我删除了lema.drae.es的所有cookie,则GET请求将返回上述隐藏表单。

简而言之,我需要一种方法从Greasemonkey中接收该POST请求的响应,我相信如果可以从服务器接收cookie并存储它,那么我可以将其作为请求头包含在其中进一步的请求,它应该按照我的预期工作。或者它应该只是存储在浏览器中,因此当我触发GM_xmlhttpRequest时它将作为标题发送。

我为我的问题尝试了不同的解决方案,即using a hidden iframe,但是,即使在将用户脚本配置为在两个域上运行之后,浏览器也会根据同源策略阻止创建此类iframe。

希望我已经明确了我想要达到的目标,我希望有人可以指出我正确的方向。

旁注:如果有人能解释challenge()函数计算的内容,我会非常感激。我的假设是它生成的令牌被发送到服务器,服务器又使用它来生成cookie,但听起来太复杂了......

1 个答案:

答案 0 :(得分:0)

隐藏的iframe路线是可行的方式,但在这种情况下, translate.google.com 会阻止它。

这是另一种方法,可确保Firefox拥有所需的全新Cookie,以保持您的mashup网站(lema.rae.es)满意:

  1. 查找mashup网站需要新Cookie时存在的一些源HTML,否则不存在。
    在这种情况下,JS源function challenge将执行。

  2. GM_xmlhttpRequest设置为mashup站点并测试响应。

  3. 如果GM_xmlhttpRequest 响应具有所需数据,请根据需要进行解析。
    完成!

  4. 如果GM_xmlhttpRequest 响应中有"需要Cookie"源,在弹出窗口中打开mashup站点的特殊查询:

    1. 由于该网站在其自己的窗口中打开,因此不会被跨域保护措施阻止。
    2. 将GM脚本设置为也对此特殊网址进行操作。
    3. 对于特殊URL,请等到存在关键节点或文本,表明页面已完成加载并设置了cookie。
    4. 弹出窗口完成后,会向开始页面发送一条消息,然后自行关闭。
    5. 当开始页面收到消息时,它会重新GM_xmlhttp请求mashup页面。
    6. 解析它并完成!

  5. 这是一个完整且经过测试的(在Firefox上)Greasemonkey脚本,它将 lema.rae.es/drae/srv/search 混合到 translate.google.com 。 :

    // ==UserScript==
    // @name     _GM_xmlhttpRequest that needs cookie(s)
    // @include  https://translate.google.com/*
    // @include  http://lema.rae.es/drae/srv/search?val=openedByGM
    // @require  http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
    // @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
    // @grant    GM_xmlhttpRequest
    // ==/UserScript==
    
    //--- Global variables
    var mashupURL   = "http://lema.rae.es/drae/srv/search?val=test";
    var cookGenURL  = "http://lema.rae.es/drae/srv/search?val=openedByGM";
    
    if (location.href == cookGenURL) {
        //--- May be best we can do until Greasemonkey fixes tab handling flaws.
        document.title = "Close me!";
    
        if (window.opener) {
            waitForKeyElements ("i:contains(openedByGM)", closePopupIfCan);
        }
    }
    else {
        attemptMashup ();
    
        window.addEventListener ("message", receiveCookieMessage, false);
    }
    
    //-- Just functions from here on down...
    
    function closePopupIfCan (jNode) {
        window.opener.postMessage ("Cookie(s) should be set!", "*");
        window.close ();
    }
    
    function attemptMashup () {
        GM_xmlhttpRequest ( {
            method:     "GET",
            url:        mashupURL,
            onload:     parseDictionaryResponse,
            onabort:    reportAJAX_Error,
            onerror:    reportAJAX_Error,
            ontimeout:  reportAJAX_Error
        } );
    }
    
    function receiveCookieMessage (event) {
        if (event.origin != "http://lema.rae.es")     return;
    
        console.log ("message ==> ", event.data);
    
        /*--- Now that have cookie(s), re-attempt mashup, but need a little
            settling time.
        */
        setTimeout (attemptMashup, 888);
    }
    
    function parseDictionaryResponse (respObject) {
        if (respObject.status != 200  &&  respObject.status != 304) {
            reportAJAX_Error (respObject);
            return;
        }
        /*--- If the required cookie is not present/valid, open the target page
            in a temporary tab to set the cookies, then reload this page.
    
            The test string is unique to the scraped site and is only present
            when cookie(s) is/are needed.
        */
        if (/function\s+challenge/i.test (respObject.responseText) ) {
            var newTab  = window.open (cookGenURL);
            return;
        }
    
        //--- Don't use jQuery to parse this!
        var parser      = new DOMParser ();
        var responseDoc = parser.parseFromString (
            respObject.responseText, "text/html" // Firefox only, for now.
        );
    
        //--- Get site-specific payload and put in site-specific location.
        var payload     = responseDoc.querySelectorAll ("body > div");
        $("#gt-form-c").before (payload);
    }
    
    function reportAJAX_Error (respObject) {
        alert (
            'Error ' + respObject.status + '!  "' + respObject.statusText + '"'
        );
    }