Разрешение двум сайтам общаться, чтобы знать текущий URL-адрес iframe - PullRequest
8 голосов
/ 19 марта 2019

Я пытаюсь найти решение, позволяющее веб-сайту узнать, по какому URL-адресу пользователя проходит через iframe.

Веб-сайт 1: http://website.website.com (Удаленный веб-сайт, можно добавить только javascript & html на веб-страницу)

Веб-сайт 2: https://example.com (полностью редактируемый, php, html, js и т. Д.)

Текущий код: (веб-сайта 2 (Example.com)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Website.com</title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</head>
<body class="body_blank">
    <script type="text/javascript">

        jq = jQuery.noConflict();

        jq(document).ready(function() {

            var currentFramePath = '';
            var iframe = '<iframe src="{src}" id="#iFrameContainer" style="position:fixed; top:0px; bottom:0px; right:0px; width: 100%; border: none; margin:0; padding:0; overflow: hidden; z-index:999999; height: 100%;">';

            var urlFrame = getUrlParameter('currentFrame');

            if(urlFrame != null && urlFrame != ''){
                console.log("Frame not found");
                jq('#iFrameContainer').html(iframe.replace('{src}', urlFrame));
                currentFramePath = urlFrame;
            }

            jq('#iFrameContainer').click(function(){
                console.log("Clicked in frame");
                currentFramePath = jq(this).attr('href');
                console.log(currentFramePath);
            });

            setInterval(function(){
                window.location = window.location.href.split('?')[0] + '?currentFrame=' + currentFramePath;
                console.log("Update Query");
            }, 5000);

        });

        function getUrlParameter(sParam) {
            var sPageURL = decodeURIComponent(window.location.search.substring(1)),
                sURLVariables = sPageURL.split('&'),
                sParameterName,
                i;
            console.log("Get Query");   
            for (i = 0; i < sURLVariables.length; i++) {
                sParameterName = sURLVariables[i].split('=');

                if (sParameterName[0] === sParam) {
                    return sParameterName[1] === undefined ? true : sParameterName[1];
                }
            }
        };
    </script>
    <div id="wrapper" class="wrapper_blank">
        <iframe src="http://website.website.com" id="#iFrameContainer" style="position:fixed; top:0px; bottom:0px; right:0px; width: 100%; border: none; margin:0; padding:0; overflow: hidden; z-index:999999; height: 100%;">
    </div>
</body>
</html>

Задача

Если я обновляю страницу (iframe) на example.com, она обновляет и забывает страницу, на которой был / был пользователь ...

Как вы можете видеть, я пытался заставить его работать, обнаруживая их страницу через iFrame, однако это невозможно из-за того, что он находится в другом домене.

Решение

Я ищу какое-то решение для чего-то подобного описанному ниже, имея в виду, что может быть лучшее решение.

Я хочу, чтобы веб-сайт website.website.com получил текущий путь / URL-адрес страницы, на которой находится пользователь (которая просматривается через iframe), и чтобы он отправил этот путь / URL-адрес на example.com, тогда пример. com будет обновлять сеанс / временный файл cookie / временное локальное хранилище / переменную ... и т. д., что будет означать, что он настроит строку запроса так, чтобы она указывала на правильный URL-адрес, когда пользователь обновляет свою страницу, в результате чего обновление корректно запоминает страницу они были включены.

Попытка

Я попытался использовать функцию postMessage, разместив следующий код на соответствующих сайтах:

Сайт 1 Дополнительный код

<script type="text/javascript">
    setInterval(function() {
        parent.postMessage(window.location.pathname, "https://website.com");
    },1000);
</script>

Сайт 2 Дополнительный код:

var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

eventer(messageEvent, function(e) {
    console.log('Parent Message: ', e.data);
}, false);

Однако ничего не происходит, никаких консольных сообщений или ошибок ... просто ничего.

Я даже пытался скопировать подобные https://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage, но ничего в этом не помогло: (

Есть идеи, что я делаю неправильно, и как решить эту проблему, чтобы добиться этого?

Спасибо

Редактирование

Я попробовал следующие js внутри http://website.website.com, но это не сработало:

localStorage.setItem('CurrentURLChecker', window.location.href)

if (localStorage.getItem('CurrentURLChecker')) {
    if (window.parent.location.href == "https://website.com/" ) {
        console.log("URL FOUND");
    }
}

Uncaught DOMException: заблокирован фрейм с источником "http://website.website.com" от доступа к фрейму перекрестного происхождения в http://website.website.com/:251:44

РЕДАКТИРОВАТЬ - Пример

Веб-сайт 1 = "http://stackoverflow.serviceprovider.com"

Веб-сайт 2 = "https://stackoverflow.com"

Веб-сайт 2 содержит iframe, который показывает, что именно показывает Веб-сайт 1.

Я никогда не собираюсь посещать Веб-сайт 1, все клики осуществляются на Веб-сайте 2

Если мне нужно было щелкнуть ссылку внутри iframe, и она должна была перейти к: http://stackoverflow.serviceprovider.com/this-new-page/, тогда Веб-сайт 1 сможет обнаружить это, сохранить местоположение iframe и запомнить его.

Теперь, если я обновлю свой браузер вместо загрузки iframe http://stackoverflow.serviceprovider.com, он вместо этого загрузит фактически обновленную страницу, которая составляет http://stackoverflow.serviceprovider.com/this-new-page/

URL-адрес вкладки / окна всегда будет оставаться на https://stackoverflow.com/, но было бы необходимо добавить строку запроса, чтобы ссылки могли быть разделены.

Это так просто.

Ответы [ 8 ]

0 голосов
/ 28 марта 2019

1, вам нужно, чтобы iframe показывал тот же URL-адрес даже после перезагрузки

2, iframe и родительское перекрестное происхождение

3, вы можете ввести js в страницы iframe

4родительская страница полностью под контролем

enter image description here

проверить https://github.com/postor/iframe-url-remember

npm i && npm run start и посетить http://localhost:3000

postMessage работает, я объясню подробно позже, мне нужно поймать шину

Я использую узел для обслуживания статического и имитирующего перекрестного происхождения, так что вы можете использовать nginx apache или php serve для размещения общей папки,и использовать локальный IP-адрес и местный хост, имитирующий перекрестное происхождение, вам может потребоваться изменить некоторые src

public / js / index.js для родительской страницы

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event) {
  console.log(event)
  localStorage.setItem('iframesrc', event.data)
}

var src = localStorage.getItem('iframesrc')
src && (document.getElementById('iframe').src = src)

1.послушайте событие сообщения, всякий раз, когда приходит новый URL-адрес, записывайте его в localStorage

2. при загрузке страницы считывайте URL-адрес из localStorage и изменяйте srciframe

public / js / iframe.js для страниц внутри iframe

window.parent.postMessage(location.href, '*');

1. при загрузке страницы отправьте URL на родительскую страницу

это просто и работает

вы можете использовать cookie вместо localalstorage, тогдавы можете использовать php update iframe src перед отправкой в ​​клиентский браузер

или сеансом php, вам может потребоваться активировать ajax для уведомления сервера при каждом изменении URL

0 голосов
/ 26 марта 2019

Несколько указателей, прежде чем мы начнем, когда у вас возникнут проблемы, всегда хорошо сначала проверить следующие основы.

Основное решение проблем

  1. Сделайте минимальное доказательство концепции, которая только показывает проблему и ничего больше. Удалите все лишние разметки, стили и код.
  2. Убедитесь, что ваши библиотеки обновлены (вы используете jquery 1.11.3 вместо 3.3.1).
  3. Следуйте стандартам, соглашениям, передовым методам, если вы плывете вверх по течению, вам только становится труднее для себя.

Рекомендации, использованные в этом ответе

Вам рекомендуется следовать им, их называют лучшими практиками, потому что они облегчают жизнь, а не усложняют ее.

  1. теги скрипта идут внизу страницы
  2. инкапсулирует все ваши собственные сценарии с помощью самостоятельно выполняемого функционального блока, чтобы не загрязнять глобальное пространство имен
  3. с использованием популярного и известного $ в качестве ссылки на jQuery, чтобы все понимали друг друга
  4. с использованием use strict директива javascript заранее предупредит о проблемных областях
  5. терминология
    1. parent - относится к основному документу в окне браузера с пометкой iframe
    2. child - относится к документу внутри родительского iframe

Cross frame access - ответ

Доступ к дочернему документу из родительского документа

Для доступа к дочернему документу от родителя iframe мы используем iframe.contentWindow . Когда у нас появляется окно iframe, мы получаем доступ к дочернему документу с iframe.contentWindow.document

Доступ к родительскому документу из дочернего документа

Для доступа к родительскому элементу iframe из дочернего документа мы используем window.frameElement . Когда у нас есть родительский элемент iframe, мы можем получить доступ к родительскому документу с помощью window.frameElement.ownerDocument .

Базовый пример

К сожалению, ваши примеры настолько запутаны с многочисленными проблемами, выходящими за рамки этого вопроса, что я был вынужден воссоздать эти страницы, чтобы облегчить их в качестве примеров.

В этих примерах показано получение местоположения как ребенка, так и родителя от ребенка или родителя и наоборот.

Родитель - test.html

Обратите внимание на идентификаторы span parentOut и childOut, которые заполняются jQuery.

<!DOCTYPE html>
<html>
<head>
    <title>Website.com</title>
</head>
<body>
    <h1>Parent page</h1>
    <span>Parent location: <span id="parentOut"></span></span><br>
    <span>Child location: <span id="childOut"></span></span><br>

    <div id="wrapper">
        <iframe src="test_child.html" id="#iFrameContainer" width="100%" height="300"></iframe>
    </div>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script>
// script encapsulation
(function ($) { "use strict";

// jQuery ready
$(function() {

  $('#parentOut').text(document.location);
  $('#childOut').text($('iframe')[0].contentWindow.document.location);

  // the iframe by tag name
  console.log($('iframe')[0]);
  // the iframe by id
  console.log($('#iFrameContainer')[0]);
  // the iframe window
  console.log($('iframe')[0].contentWindow);
  // the child document
  console.log($('iframe')[0].contentWindow.document);

});

})(jQuery);
    </script>
</body>
</html>

Дитя - test_child.html

Обратите внимание на идентификаторы span parentOut и childOut, которые заполняются jQuery. Есть также несколько гиперссылок страниц, которые НЕ БУДУТ работать, см. Тему Политики безопасности .

<!DOCTYPE html>
<html>
<head>
    <title>Website.com</title>
</head>
<body>
    <h1>Child page</h1>
    <span>Child location: <span id="childOut"></span></span><br>
    <span>Parent location: <span id="parentOut"></span></span><br>
    <h3>Some child pages that DON'T work</h3>
    <a href="http://my.umt.edu">SecurityError: Protocols, domains, and ports must match.</a><br>
    <a href="https://en.wikipedia.org/">SecurityError: Protocols must match.</a><br>
    <a href="https://www.google.com">X-Frame-Options SAMEORIGIN</a><br>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script>
// script encapsulation
(function ($) { "use strict";

// jQuery ready
$(function() {
  $('#childOut').text(document.location);
  $('#parentOut').text(window.frameElement.ownerDocument.location);

  // parent iframe
  console.log(window.frameElement);
  // parent document
  console.log(window.frameElement.ownerDocument);

});

})(jQuery);
    </script>
</body>
</html>

Получать уведомления об изменениях местоположения ребенка

Чтобы получать уведомления об изменениях местоположения в дочернем документе, мы можем использовать события onload или onloadstart для уведомления родителя.

$(document).on('load' function (event) {
    $(window.frameElement.ownerDocument).append($('<p>').text('The location changed to:'+this.location);
});

Политики безопасности

Как мы видим, эта функциональность довольно мощная, поскольку она полностью выставляет родительские и дочерние документы друг другу и наоборот. Поскольку это позволяет вам управлять контентом, существуют политики безопасности, которые не позволяют нам манипулировать целостностью контента, которым мы не владеем.

Протоколы, домены и порты должны совпадать

Существует несколько иная формулировка для похожих ошибок, но все они сводятся к тому, что дочерняя страница должна иметь то же доменное имя, тот же порт и использовать тот же протокол, что и родительский, или доступ заблокирован. Первые два примера на дочерней странице будут возвращать эти ошибки соответственно.

SecurityError: заблокирован фрейм с источником "http://127.0.0.1:1221" от доступа к фрейму с источником" http://my.umt.edu". Протоколы, домены и порты должны совпадать.

SecurityError: заблокирован фрейм с источником "http://127.0.0.1:1221" от доступа к фрейму с источником" https://en.wikipedia.org". фрейм, запрашивающий доступ, имеет протокол "http", фрейм, к которому осуществляется доступ, имеет протокол "HTTPS". Протоколы должны совпадать.

Эти страницы разрешено просматривать в iframe, но если и только если дочерние элементы расположены в http://127.0.0.1:1221 (в моем случае), эта функциональность будет разрешена.

Еще больше безопасности

Мы также можем полностью запретить просмотр наших сайтов в iframe. С помощью заголовка ответа X-Frame-Options http, если он настроен на SAMEORIGIN, браузер отклонит загрузку страницы во фрейме. Смотрите последний пример на дочерней странице.

Заключение

Гораздо проще выяснить, в чем именно заключается проблема, если мы отложим наш проект в сторону и начнем заново только с проблемных частей. Это также значительно облегчает помощь и дает полезный ответ.

Из того, что я понимаю в вашем случае использования, то, что вы хотите сделать, запрещено. Вы можете свободно использовать фреймы на своем собственном сайте со своими собственными страницами, но запрещается манипулировать чужим контентом.

NJoy!

0 голосов
/ 27 марта 2019

Хотя, исходя из предположения, что вы можете получить доступ и редактировать второй веб-сайт, сервер не поддерживает PHP или любой другой язык программирования / сценариев, и вы застряли в HTML и Javascript:

На родительской странице PHP, в которую вы встраиваете iframe, вы можете вызвать iframe с добавленным параметром, как показано ниже:

<iframe src="http://website.website.com/example.html?parent=<?=$_SERVER['HTTP_REFERER'];?>"></iframe>

Затем на дочерней html-странице вы можете перехватить параметр, переданный методом GET с помощью JavaScript или jQuery, и использовать его для определения страницы, как показано ниже:

<script>
$(document).ready(function(){
    var urlParams = new URLSearchParams(window.location.search);
    var parentPage = urlParams.get('parent'); //which will store "https://example.com" in the variable. Now that you have the parent page URL you can manipulate it.
});
</script>

Даже если вы не можете редактировать html, вы можете внедрить JavaScript и HTML в DOM страницы iframe через родительскую страницу и сразу же запустить его, объявив его в функции jQuery следующим образом:

(function() {
    var urlParams = new URLSearchParams(window.location.search);
    var parentPage = urlParams.get('parent');
})();

Надеюсь, это имело хоть какой-то смысл и может быть полезным в любом случае. Удачи в вашем квесте.

Ура!

0 голосов
/ 23 марта 2019

Вы можете использовать пиксель отслеживания и передать текущий путь iframe в качестве параметра:

var pathname = window.location.pathname;
var d = new Date();
var imageUrl = 'http://www.example.com/trackingpixel.php?path=' 
    + pathname + '&time=' + d.getTime();

var img = document.createElement('img');
img.src = imageUrl;
document.body.appendChild(img);

И в родительском домене создать маршрут trackingpixel.php и сохраните текущий путь в сеансе:

if( !isset($_SESSION['time']) || ($_GET['time'] > $_SESSION['time'])) {
    $_SESSION['time'] = $_GET['time'];
    $_SESSION['path'] = $_GET['path'];
}

Затем, когда вы перезагрузите страницу, вы можете получить путь из сеанса:

if(isset($_SESSION['path'])) {
   $iframeUrl = $_SESSION['path'];
}
else {
   $iframeUrl = 'http://website.website.com';
}

Обратите внимание, что этоНебольшая вероятность того, что это не сработает, если перезагрузка выполняется до пикселя отслеживания от предыдущей загрузки.

PS: В настоящее время расширения рекламных блоков довольно популярны, и они могут предотвращать "срабатывание" пикселя , я бы посоветовал проверить, работает ли пиксель с некоторыми из популярных расширений.

0 голосов
/ 22 марта 2019
  • Примеры кода с http://website.website.com и https://example.com не работают, потому что существуют разные схемы URI. Один http, а другой https.

  • Итак, они должны быть по одному и тому же протоколу HTTP, чтобы это работало (либо http, либо оба https).

  • В моем примере я использую URL родительского окна как https://parent.example.com и URL iframe как https://child.somesite.com.

В коде iframe:

  • Когда сайт iframe загружается, мы собираемся отправить postMessage () на родительский сайт о текущем URL, назначив прослушиватели событий с помощью addEventListener для привязки тегов, когда бы они ни были щелкают.

  • Таким образом, когда щелкают тег привязки, мы предотвращаем поток маршрута по умолчанию, отправляем сообщение о текущем URL-адресе в родительское окно и устанавливаем href текущего окна в href привязки.

Код:

var a_tags = document.getElementsByTagName('a');

for(var i=0;i<a_tags.length;++i){
    a_tags[i].addEventListener('click',function(event){
        event.preventDefault();
        var current_href = this.getAttribute('href');
        var new_location = current_href.match(/^http(s)?:\/\/.+$/) !== null ? current_href : window.location.origin + current_href;// be careful about leading '/' when dealing with relative URLs. 
        window.parent.postMessage(new_location,'https://parent.example.com');
        window.location.href = new_location;
    });
}

В родительском окне код:

  • Здесь мы просто прикрепим прослушиватель событий к событию message и проверим, было ли событие запущено с самого нашего дочернего сайта, используя реферер, присутствующий в event.origin.

  • Если это не так, мы вернемся. Если это так, мы обновляем наш localStorage и устанавливаем полученный URL-адрес на ключ iframe_url.

  • При обновлении страницы мы сначала проверяем, установлен ли localStorage этот ключ или нет. Если нет, мы загружаем iframe как есть, иначе мы загружаем URL, который есть в нашем хранилище, устанавливая его атрибут src.

  • Обратите внимание, что мы создаем элемент iframe из javascript, чтобы избежать добавления отдельных обработчиков событий для обработки его src при запросе на новой вкладке в окне.

Код:

const IFRAME_SITE_DOMAIN = 'https://child.somesite.com';

window.addEventListener('message',function(event){
    if(event.origin !== IFRAME_SITE_DOMAIN) return;
    localStorage.setItem('iframe_url',event.data);
});

var iframe = document.createElement('iframe');

if(localStorage.getItem('iframe_url') === null){
    iframe.setAttribute('src',IFRAME_SITE_DOMAIN);
}else{
    iframe.setAttribute('src',localStorage.getItem('iframe_url'));
}

iframe.setAttribute('height','500');
iframe.setAttribute('width','500');

document.body.append(iframe);

Sharable Links:

  • Мы создаем кнопку и диапазон для общих действий пользователя, например так.

Код:

<button id='share_resource_state'>Share Link</button>
<span id='share_url'></span>
  • Теперь мы добавляем текущий URL-адрес iframe во фрагменты URL (символы после #). Поскольку мы добавляем это во фрагмент, нам не нужно беспокоиться о его влиянии на стороне сервера родительского сайта, поскольку он никогда не отправляется на сервер и играет роль исключительно в браузере клиента.

  • Мы преобразуем URL-адрес iframe в base64 с использованием btoa() при совместном использовании и декодируем его с помощью atob() при запросе на новой вкладке или в окне.

  • Это немного изменяет текущий код на родительском сайте (главном окне).

Код:

const IFRAME_SITE_DOMAIN = 'https://child.somesite.com';

window.addEventListener('message',function(event){
    if(event.origin !== IFRAME_SITE_DOMAIN) return;
    localStorage.setItem('iframe_url',event.data);
});

var iframe = document.createElement('iframe');

if(localStorage.getItem('iframe_url') === null){
    if(window.location.hash != ''){
        try{
            var decoded_string = atob(window.location.hash.substring(1));// to remove the # from the fragment and get the base64 encoded data.
            if(decoded_string.indexOf('iframe_url=') !== -1){
                iframe.setAttribute('src',decoded_string.split('=')[1]);// we split the string based on '=' and assign the iframe URL which was set at the time of sharing
            }else{
                iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we don't deal with the fragment at all since it isn't encoded for our iframe purpose.
            }
        }catch(e){
            iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we don't deal with the fragment at all.
        }
    }else{
        iframe.setAttribute('src',IFRAME_SITE_DOMAIN); // we set URL as is.
    }
}else{
    iframe.setAttribute('src',localStorage.getItem('iframe_url'));
}

iframe.setAttribute('height','500');
iframe.setAttribute('width','500');

document.body.append(iframe);

document.getElementById('share_resource_state').addEventListener('click',function(){
    var iframe_sharable_url = localStorage.getItem('iframe_url') === null ? IFRAME_SITE_DOMAIN : localStorage.getItem('iframe_url');
    document.getElementById('share_url').innerHTML = window.location.href.split('#')[0] + '#' + btoa('iframe_url=' + iframe_sharable_url);
});
0 голосов
/ 22 марта 2019

Кажется, что targetOrigin (второй аргумент postMessage) может просто не совпадать. Не забывайте, что протокол, хост и порт должны точно совпадать.

Из разметки, которую вы разместили, домен iframe src равен http://website.website.com, а родительский домен - https://example.com.

Если вы хотите, чтобы http://website.website.com сообщал свой URL-адрес https://example.com, то отправка сообщения из iframe должна выглядеть следующим образом:

window.parent.postMessage(window.location.pathname, 'https://example.com');

Чтобы убедиться, что фильтр targetOrigin не является причиной проблем со связью, вы также можете использовать * для тестирования.

Похоже, что вы делаете противоположное в своем примере (передача исходного домена вместо целевого домена), и это также очень вводит в заблуждение, что вы используете «сайт 1» для ссылки на встроенный сайт и «сайт 2» для ссылки на родительский сайт в вашем объяснении: я бы ожидал обратного.

0 голосов
/ 19 марта 2019

По соображениям безопасности вы можете получить URL только в том случае, если содержимое iframe и ссылочный javascript обслуживаются из одного домена.

Если два домена не совпадают, вы столкнетесь с ограничениями безопасности сценариев межсайтовых ссылок.

0 голосов
/ 19 марта 2019

Поскольку вы можете добавить javascript на веб-сайт 1 (http://website.website.com), вы можете создать сеанс с использованием javascript и сохранить текущую страницу, которую пользователь посещает в файлах cookie (как описано здесь ).Когда пользователь заходит на домашнюю страницу веб-сайта (что происходит, когда пользователь перезагружает веб-сайт 2), вы можете получить это значение с помощью javascript и загрузить сохраненную страницу (window.location.href = 'http://website.website.com/YourSavedPage').

Если вы неЕсли вы хотите, чтобы такое перенаправление происходило каждый раз, когда пользователь посещает домашнюю страницу веб-сайта 1, вы можете подумать о создании собственной страницы, чтобы перенаправить пользователя на последнюю открытую страницу и открыть эту страницу один раз при загрузке iframe.

...