Обмен данными между доменами. Часть 2.
Эта статья является продолжением статьи Обмен данными для документов с разных доменов и рассматривает новые способы обмена данными между доменами, которые, однако, уже поддерживаются основными современными браузерами.
В стандарте HTML5 предусмотрена отсылка javascript-сообщения из одного окна в другое при помощи специального вызова window.postMessage .
При этом окна могут быть с любых доменов, не важно. Например, один iframe с yandex.ru , а другой - с vaysapupkin.info. Чтобы получать кросс-доменные сообщения, принимающее окно регистрирует специальный обработчик onmessage , в котором, кроме всего прочего, может проверить, с какого домена пришло сообщение.
В примере ниже используется iframe с домена javascript.ru, исходник которого приведен ниже, и iframe с домена ilyakantor.ru.
Как вы можете видеть, сообщение прекрасно пересылается. Пример работает в браузерах Firefox 3.5, Opera 10, Safari 4.0/Chrome 3.0+, Internet Explorer 8.0.
Исходник окна, отправляющего сообщение:
<html>
<body>
<iframe src="http://ilyakantor.ru/receive.html" id="iframe"></iframe>
<form id="form">
<input type="text" id="msg" value="Ваше сообщение"/>
<input type="submit"/>
</form>
<script>
var win = document.getElementById("iframe").contentWindow;
document.getElementById("form").onsubmit = function(){
win.postMessage(
document.getElementById("msg").value,
"http://ilyakantor.ru" // target domain
);
return false
}
</script>
</body>
</html>
Как видите, для передачи сообщения достаточно вызвать метод postMessage(message, targetOrigin) принимающего окна.
message
- Сообщение - javascript-объект
targetOrigin
- Целевой домен, с которого должно быть окно, получающее сообщение. Если вы не хотите ограничить целевой домен - поставьте вместо него звездочку "*".
Исходный код принимающего окна (в ифрейме):
<html>
<body>
<b>iframe с домена http://ilyakantor.ru</b>
<div id="test">Пришли мне сообщение!</div>
<script>
function listener(event){
if ( event.origin !== "http://javascript.ru" )
return;
document.getElementById("test").innerHTML = event.origin + " прислал: " + event.data;
}
if (window.addEventListener){
window.addEventListener("message", listener,false);
} else {
window.attachEvent("onmessage", listener);
}
</script>
</body>
</html>
Обработчик события onmessage проверяет исходный домен, который находится в свойстве event.origin , и, если все нормально, выводит на экран сообщение event.data .
Все прозрачно и удобно.
Реализация postMessage браузерами использует внутренний механизм передачи сообщений, поэтому можно передавать любые объекты, а сам факт передачи никак не отслеживается снифферами пакетов.
Интерфейс поддерживается основными современными браузерами.
Уже давно в W3C вызревает второй стандарт XMLHTTPRequest 2. А пока суть да дело - Firefox 3.5, Safari 4/Chrome 3 и IE8 уже реализовали ряд фич.
Например, кросс-доменные запросы.
Следующий пример отправляет запрос с текущего домена (javascript.ru) на домен ilyakantor.ru.
Он не работает в старых браузерах и... в Opera.
Кросс-доменный запрос отсылается точно так же, как и обычный XMLHttpRequest, но браузер посылает на сервер дополнительный заголовок Origin .
Сервер в ответе указывает заголовком Access-Control-Allow-Origin , для каких доменов доступны данные. Ответ сервера:
Если вдруг домен, на который приходит ответ, не совпадает с Access-Control-Allow-Origin (можно указать звездочку "*" для любого домена), то браузер блокирует операцию из соображений безопасности.
В то время как Firefox/Safari расширили обычный объект XMLHTTPRequest, специалисты Microsoft завели для кросс-доменных запросов новый объект XDomainRequest.
Оба объекта имеют весьма схожие интерфейсы, но решение Firefox/Safari более полно реализует XMLHTTPRequest 2. В частности, поддерживаются другие методы, кроме GET/POST, работа с Cookie/HTTP Auth и расширены средства контроля доступа.
Исходный код отсылающего запрос ифрейма:
<html>
<head>
<script type="text/javascript">
var url = 'http://ilyakantor.ru/xdr/receive.php';
function doCallOtherDomain(){
var XHR = window.XDomainRequest || window.XMLHttpRequest
var xhr = new XHR();
xhr.open('GET', url, true);
// замена onreadystatechange
xhr.onload = function() {
document.getElementById('response').innerHTML = xhr.responseText
}
xhr.onerror = function() {
alert("Error")
}
xhr.send()
}
function callOtherDomain() {
try {
doCallOtherDomain()
} catch (e) {
alert("В этом браузере данная фича не поддерживается.")
}
}
</script>
</head>
<body>
<div id="response"></div>
<input type="button" value="Нажмите для запроса к другому домену" onclick="callOtherDomain()" />
</body>
</html>
Принимающий код:
<?php
// можно ограничить домен, для которого доступен ответ
// header('Access-Control-Allow-Origin: http://javascript.ru');
header('Access-Control-Allow-Origin: *');
?>
http://ilyakantor.ru Ха-ха! Все получилось!
Как видно из примера, в обоих реализациях можно отказаться от onreadystatechange и использовать события onload/onerror/onabort .
Кроме того, начиная с Firefox 3.5/Safari 4/IE8 поддерживается событие onprogress , предназначенное для реализации progress bar.
Конечно, можно написать кросс-браузерную обертку, которая будет прозрачно отправлять запросы, используя эти новые методы в дополнение к уже существующим.
Мы разобрали основы новых способов кросс-доменной коммуникации. При практической реализации вам могут понадобиться детали.
Могу порекомендовать следующие статьи.
|
то есть получается, что с помощью этой фичи можно xss атаки проводить еще проще... например, передать куки с одного домена ну другой ?
Если скрипт будет отдавать что попало кому попало, то да.
Именно этим разработчики оперы мотивируют категорическое нежелание делать возможность обычных кросс-браузерных запросов.
Кросс-доменные запросы
Ты хотел сказать кроссайтовые т.е. крос доменные a.com b.com
Гость что отпостил выше и viglim:
вы как бы не заметили строчку про то, что сервер к которому делается кросс запрос (уточнение идет даже относительно скрипта на серваке) должен разрешать делать к себе запросы, причем кого то может игнорировать.
поэтому уже безопасность будет зависить от прямоты рук тех, кто использует данный заголовок.
Прикольно полдня просидел в поисках решения передачи запроса с одного своего сайта на другой и вуаля... Автору спасибо. От себя замечу: простой скриптик всего в несколько строчек работает на Мозиле (3.6) у меня без всяких выкрутасов (в других браузерах пока не проверял), всё, что здесь написано - не заработало у меня, хотя примеры на этой странице работают, как и написано. Эх, лень копаться в глюках.
Для передачи параметров на другой сайт всего лишь прописал строку, указанную в статье и всё:
header('Access-Control-Allow-Origin: *');
Для передачи параметров на другой сайт всего лишь прописал строку, указанную в статье и всё:
header('Access-Control-Allow-Origin: *');
А куда прописал? У меня похожая ситуация, получаю от сервера Origin "null" и пустой ответ. Подскажите.
ИЕ 7 и ИЕ 6 еще долго будут портить кровь программистам, не давая возможности использовать эти фишки, а это чуть больше 10% по статистики li
То есть таким способом, можно читать xml файлы с других доменов ?
XMLHTTPRequest 2 если не ошибаюсь, если делает запрос на другой домен, перед этим сначало запрашивает ту же страницу с методом OPTION. а уже потом идет запрос либо GET либо POST либо что то еще.
Здесь, при нажатии Отправить, сообщение посылается во фрейм. Если добавить второй фрейм с каким-либо сайтом, как послать в первый фрейм вместо сообщения адрес из второго фрейма?
С локального файла не работает - надо обязательно загрузить на реальный сервер
То есть убогий костыль!
Интересно что через яховский прокси работает отвосюду в том числе и с локального файла.
XMLHTTPRequest 2 / XDomainRequest
В недавно вышедшей опере 12.00 сделали
подскажите пожалуйста очень нужно. А как правильно формировать обращение к окну, если нам надо наоборот отправить обращение из ифрейма к перент окну? или как правильно обратиться к не вложенному, а соседнему ифрейму? (вопрос по способу postMessage)
Для отправления сообщения родительскому окну используйте конструкцию вида top.postMessage. А вот к соседнему фрейму, насколько я представляю себе модель безопасности при кросдоменных запросах, можно только через родительское окно.
Но лучше не изобретать велосипеды, а использовать библиотеку типа easyXDM (так же есть плагин для jQuery для работы с postMessage). Там все это реализовано довольно удобно, в том числе механизм удаленного вызова процедур, который является надстройкой над вот таким транспортом (или другим, если браузер не поддерживает его).
Ребят... Curl вам в помощь... Отправляете запрос на php на своем домене, Curl отправляет запрос на другой и возвращает ответ Вам... Если бы опера еще работала с этим, то было бы норм. А так через фреймы или отправлять Ajaxом. Ну нафиг. На php это 5 строчек и полная кроссбраузерность))
Это далеко не всегда и не всем подходит. Иногда нужно использовать именно клиентский вариант.
Curl, wget и любые другие серверные качалки для разовых задач, при этом нагрузка на сервер, что облегчается и ускоряется Ajax-ом.
Помогите, пожалуйста, разобраться с такой вот проблемой.
Делается кроссдоменный POST-запрос по отправке файла, в ответе идет строка с идентификатором добавленного объекта (Content-type: text/plain).
Все браузеры адекватно эту строку вынимают при помощи xhr.responseText, а вот у IE10 в xhr.responseText всегда пусто.
Что там может быть такое?
IE11, FR29.0.1
В этом браузере данная фича не поддерживается.
CR34.0
Всё ok