Статьи

Работа с одной политикой и вокруг нее

Как разработчик JavaScript, вы, вероятно, широко используете Ajax для обмена данными с сервером или обновления веб-страницы без обновления. Хотя отправка запроса Ajax на ваш сервер является довольно простым запросом, обмен данными с сервером в другом домене — это совсем другая история. Давайте попробуем это!

Давайте запустим следующее с http://www.mysite.com (или 127.0.0.1 / localhost ) в Chrome 32.

 request = new XMLHttpRequest; request.open('GET', 'http://myothersite.com/', true); request.send(); 

Вы получите ошибку. XMLHttpRequest не может загрузить http://myothersite.com/ . В запрошенном ресурсе отсутствует заголовок 'Access-Control-Allow-Origin' . Origin http://www.mysite.com поэтому не допускается доступ.

Почему это происходит? Разве мы не сделали все правильно?

Политика одного происхождения

Политика того же источника разрешает сценариям, запущенным в браузере, отправлять запросы только на страницы в одном домене. Это означает, что запросы должны иметь одинаковую схему URI, имя хоста и номер порта. Этот пост в Mozilla Developer Network четко определяет определение источника и когда запросы приводят к сбою. Если вы отправляете запрос с http://www.mysite.com/ , следующие типы запросов приводят к сбою.

  • https://www.mysite.com/ — Другой протокол (или схема URI).
  • http://www.mysite.com:8080/myUrl — Другой порт (поскольку HTTP-запросы по умолчанию выполняются на порту 80 ).
  • http://www.myothersite.com/ — Другой домен.
  • http://mysite.com/ — Рассматривается как другой домен, поскольку требует точного соответствия (обратите внимание, что нет www. ).

Изменение происхождения

Иногда одна и та же политика происхождения может блокировать запросы между поддоменами в одном домене. Самый простой способ решить эту проблему — установить document.domain из JavaScript. Например:

 document.domain = 'mysite.com'; 

Обратите внимание, что номер порта хранится отдельно. Чтобы заставить один домен взаимодействовать с другим через другой порт (как в случае с приложениями чата), потребуется нечто иное. Даже установка document.domain = document.domain , которая перезаписывает номер порта на null , не поможет в этом.

Использование веб-прокси

Хотя указание document.domain помогает вам связываться с поддоменами вашего собственного веб-сайта, что бы вы сделали, если бы вам понадобился вообще доступ к данным из другого домена? Интересный, но простой для понимания подход заключается в использовании веб-прокси на вашем собственном сервере.

Вместо того, чтобы отправлять запрос напрямую с вашего домена ( http://www.mysite.com/ ) на новый домен ( http://www.myothersite.com/ ), вы вместо этого отправляете запрос на свой собственный сервер ( http://www.mysite.com/connect/ ), который, в свою очередь, отправляет запрос на новый домен ( http://www.myothersite.com/ ). Похоже, что в браузере вы обмениваетесь данными с вашим собственным сервером. В действительности, в фоновом режиме вы получили доступ к данным в новом домене с вашего сервера. Блок-схема, объясняющая процесс, показана ниже.

alt text
Источник: Yahoo Developers

Использование JSONP

Другим способом реализации межбраузерных запросов является использование JSONP или «JSON with padding». JSONP использует тот факт, что теги <script> не подчиняются политике одного и того же происхождения. Например, вы можете включить библиотеку, такую ​​как jQuery, на свою страницу, даже если она размещена в CDN Google.

Запросы JSONP выполняются путем динамического запроса <script> . Интересно то, что ответ JSON заключен в вызов функции. Делая запрос, вы указываете имя функции в качестве функции обратного вызова. Когда сервер отвечает, функция обратного вызова (которая должна существовать на вашей странице) выполняется с данными, возвращаемыми с сервера.

Например, типичный ответ JSON может выглядеть следующим образом:

 { "id": "123", "name": "Captain Jack Sparrow" } 

Тот же ответ можно эмулировать в теге скрипта с помощью функции обратного вызова myFunction как показано ниже.

 <script src="http://www.myothersite.com/get_data?callback=myFunction"></script> 

Браузер обычно загружает данные с указанного URL и оценивает их как JavaScript. Ответ может выглядеть так:

 myFunction({"id": "123", "name": "Captain Jack Sparrow"}); 

Затем будет вызвана myothersite.com myFunction , позволяющая вашей странице обрабатывать JSON, возвращенный с myothersite.com .

Проблемы безопасности — подделка межсайтовых запросов

Поскольку <script> не соответствует политике одного и того же источника, злоумышленник может получить конфиденциальные данные, используя один и тот же URL-адрес. Используя приведенный выше пример, вредоносная страница может загрузить те же данные JSON и выполнить с ними какое-то недоброе действие. Это известно как атака подделки межсайтовых запросов (CSRF). Некоторые меры противодействия CSRF-атакам включают использование токенов или файлов cookie для проверки и ограничение срока службы таких токенов.

Распределение ресурсов между доменами

Хотя JSONP можно использовать для выполнения большинства задач относительно легко, есть несколько недостатков. Вы можете отправлять только HTTP-запросы GET, используя JSONP. Это исключает любую возможность выполнения операций CRUD чисто с использованием JSONP. Хотя эта проблема безопасности устраняется с помощью прокси-метода, есть еще один интересный метод, который поможет нам взаимодействовать с RESTful API, не перепрыгивая через обручи.

Распределение ресурсов между доменами, или CORS, работает путем изменения заголовков HTTP в ваших запросах для доступа к ресурсам в другом домене. В IE8 + разрешены простые запросы CORS с использованием XDomainRequest (вместо XMLHttpRequest ). Простой пример показан ниже.

 request = new XDomainRequest(); request.open(method, url); request.onload = function() { callback(req.responseText); }; request.send(data); 

Мы говорили о «простых» запросах. Не столь простые запросы относятся к предварительно проверенным запросам, которые сначала отправляют HTTP-запрос в другой домен, чтобы определить, безопасно ли выполнять это действие. Подробный пример предварительного запроса и обмена информацией между сервером и клиентом объясняется в этом сообщении MDN о CORS .

Вывод

Хотя CORS выглядит как будущее для интерфейсного программирования, вы все равно должны использовать его с осторожностью, поскольку нет поддержки очень старых браузеров (IE7 и более ранних версий). Поддержка CORS — это небольшая проблема, но вы обязательно должны попробовать ее! Для дальнейшего чтения я предлагаю вам просмотреть подробный пост MDN по CORS .