предисловие
Технический результат написан в отношении обучения и обобщения, пожалуйста, укажите на любые ошибки и проблемы в тексте. Больше технических результатов можно посмотреть в моемблог на гитхабе.
Я разобрался с некоторыми внешними учебными ресурсами, надеясь помочь тем, кто в них нуждается, по адресу:Резюме учебных ресурсов.
перекрестный домен
Междоменное означает, что ресурсы с разными протоколами, доменными именами (хост) и номерами портов (почта) пытаются обмениваться данными в интерактивном режиме.Однако из-за ограничений политики браузера в отношении одного и того же источника нормальное интерактивное взаимодействие невозможно.
Самый распространенный практический сценарий - в процессе разработки проекта будут запросы на ресурсы под другими сторонними доменами.Например, при использовании API карты нужно установить белый список при установке ключа для использования карты API обычно.
При использовании AJAX для запроса ресурсов данных в разных доменах третьей стороны, если междоменная проблема не решена, HTTP-запрос не может быть успешно отправлен, и браузер выдает предупреждение об ошибке.
Та же политика происхождения
MDN объясняет: политика того же источника ограничивает взаимодействие документов или сценариев, загруженных из одного источника, с ресурсами из другого источника. Это важный механизм безопасности для изоляции потенциально вредоносных файлов.
Целью политики браузера в отношении того же источника является предотвращение вредоносных атак, таких как XSS и CSRF.
Политика того же происхождения взаимодействует тремя способами:
- Операции междоменной записи, такие как ссылки, перенаправления и т. д., обычно разрешены.
- Обычно разрешены междоменные вложенные ресурсы, такие как img, теги сценариев и т. д.
- Операции чтения из разных источников, как правило, не допускаются.
Междоменные сценарии
Только если протокол, имя домена и номер порта между ресурсами одинаковы, они являются одним и тем же источником.
Ниже приводится междоменное описание одного и того же источника и между разными источниками.
URL | иллюстрировать | Разрешить ли общение |
---|---|---|
www.demo.com/a.html www.demo.com/b.html www.demo.com/c.html |
такое же доменное имя | разрешать |
www.demo.com/news/a.html woohoo.demo.com/center/no.contract… woohoo.demo.com/server/from.contract… |
Разные папки под одним доменным именем | разрешать |
www.demo.com/a.html www.demo.com:80/b.html |
разные номера портов | не положено |
www.demo.com/a.html www.demo.com/b.html |
разные протоколы | не положено |
www.demo.com/a.html www.test.com/b.html |
разные доменные имена | не положено |
www.demo.com/a.html test.demo.com/b.html |
Основной домен тот же, поддомены разные | не положено |
междоменное решение
1. JSONP
Поскольку политика браузера с одинаковым источником позволяет вкладывать ресурсы из разных источников, такие как теги сценария, ресурсы с тегами сценария не ограничиваются политикой одного источника.
Решение JSONP состоит в том, чтобы делать междоменные запросы через теги сценария.
- Внешний интерфейс устанавливает функцию обратного вызова и рассматривает функцию обратного вызова как параметр, передаваемый URL-адресом запроса.
- После того, как серверная часть получает запрос, она возвращает имя функции обратного вызова и необходимые данные.
- После того, как серверная часть отвечает и возвращает данные, возвращенные данные передаются в функцию обратного вызова и выполняются.
<!-- 通过原生使用 script 标签 -->
<script>
function jsonpCallback(data) {
alert('获取到的数据了,打开控制台瞧瞧');
console.log(data);
}
</script>
<script src="http://127.0.0.1:3000?callback=jsonpCallback"></script>
Вы также можете использовать метод запроса AJAX GET для выполнения междоменных запросов (метод axios GET одинаков для междоменных запросов).
<!-- AJAX GET 请求 -->
<script>
function jsonpCallback(data) {
alert('获取到的数据了,打开控制台瞧瞧');
console.log(data);
}
$.ajax({
type: 'GET', // 必须是 GET 请求
url: 'http://127.0.0.1:3000',
dataType: 'jsonp', // 设置为 jsonp 类型
jsonpCallback: 'jsonpCallback' // 设置回调函数
})
</script>
Преимущества и недостатки:
-
Совместимость хорошая, и более ранние версии IE также поддерживают этот метод.
-
Поддерживаются только HTTP-запросы GET.
-
Только поддержка HTTP-запросов, таких как внешняя и внутренняя передача данных, не может решить проблему взаимодействия данных и связи между страницами в разных доменах.
2. CORS
Междоменное совместное использование ресурсов CORS позволяет осуществлять междоменное взаимодействие после выполнения соответствующих настроек на сервере.
Если на сервере не задано междоменное поле CORS, сервер отклонит запрос и выдаст предупреждение об ошибке.
Сервер устанавливает поле Access-Control-Allow-Origin. Значением может быть конкретное доменное имя или подстановочный знак «*». После настройки можно разрешить данные междоменного запроса.
<script>
$.ajax({
type: 'post',
url: 'http://127.0.0.1:3000',
success: function(res) {
alert('获取到的数据了,打开控制台瞧瞧');
console.log(res);
}
})
</script>
Как сервер устанавливает междоменные поля? Настройки внутреннего языка несовместимы между доменами. Подробную информацию см. в API самого внутреннего языка.
Узел боковые настройки
res.writeHead(200, {
'Access-Control-Allow-Origin': '*'
});
// 或者使用了 Express 这样的框架
res.header("Access-Control-Allow-Origin", "*");
Для получения подробной информации о CORS вы можете обратиться к этой заметке,Совместное использование ресурсов CORS между источниками.
3. Server Proxy
Метод проксирования запросов через сервер также является решением междоменной проблемы браузера. Политика одного и того же источника — это только политика безопасности для браузеров, и сервер не ограничивается политикой одного и того же источника, поэтому междоменной проблемы не возникает. Конкретные шаги заключаются в следующем:
- Внешний интерфейс обычно запрашивает интерфейс, предоставляемый сервером. Например, интерфейс запроса:http://localhost:3000.
- Настройте прокси на сервере для отправки запроса, а затем верните необходимые данные во внешний интерфейс после запроса данных. Например, набор интерфейсов запроса проксиСупер нет JS.org/API/V1/topi…, прокси-сервер запрашивает данные обратно, а затем отправляет данные обратноhttp://localhost:3000Интерфейс возвращен на фронтенд.
// 服务端代理请求代码
// 服务端只是简单的通过正常的 HTTP 请求的方式来代理请求接口数据
// 或者也可以使用 proxy 模块来代理,至于怎么使用 proxy 模块,待研究完善
var url = 'https://cnodejs.org/api/v1/topics';
https.get(url, (resp) => {
let data = "";
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
res.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json; charset=utf-8'
});
res.end(data);
});
})
4. location.hash + iframe
Реализация междоменной связи location.hash + iframe выглядит следующим образом:
- Страница a разных доменов взаимодействует со страницей b, а страница b встроена в страницу a через iframe, а значение хеш-функции добавляется к src iframe.
- После того как страница b получает хеш-значение, она определяет, что страница а пытается установить связь сама с собой, а затем передает данные, которые необходимо передать, хеш-значению страницы а, изменяя значение parent.location.hash.
- Однако, поскольку подстраницам не разрешено напрямую изменять хеш-значение родительской страницы в IE и Chrmoe, для передачи данных через страницу c того же домена, что и страница a, требуется прокси-страница.
- Точно так же страница c встраивается в страницу b через iframe, а передаваемые данные передаются на страницу c через хеш-значение ссылки src iframe.Поскольку страница a и страница c находятся в того же домена, c-страница может напрямую изменить хеш-значение страницы a или вызвать глобальную функцию на странице.
Общий процесс таков:
код страницы
<script>
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = "http://localhost:8081/b.html#data";
document.body.appendChild(iframe);
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : '';
console.log('获得到的数据是:', data);
}catch(e) {}
}
window.addEventListener('hashchange', function(e) {
console.log('监听到hash的变化:', location.hash.substring(1));
})
</script>
б код страницы
<script>
switch(location.hash) {
case '#data':
callback();
break;
}
function callback() {
var data = "testHash"
try {
parent.location.hash = data;
}catch(e) {
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = 'http://localhost:8080/c.html#' + data;
document.body.appendChild(ifrproxy);
}
}
</script>
c код страницы
<script>
// 修改 a 页面的 hash 值
parent.parent.location.hash = self.location.hash.substring(1);
// 调用 a 页面的全局函数
parent.parent.checkHash();
</script>
Преимущества и недостатки:
- Количество данных, передаваемых хешем, ограничено.
- Данные отображаются непосредственно в URL-адресе.
5. document.domain + iframe
Это решение ограничено междоменными решениями для ресурсов в одном основном домене и разных поддоменах.
Практические сценарии применения:
В предыдущей разработке проекта часто встречались такие междоменные проблемы, что примерно похоже на страницу продукта разработки нового продукта.Перед официальным запуском он вообще выгружается во внутреннюю тестовую среду.Например доменное имя тестовой средыtest.admin.com/xxx/xxx, а проект... consumer-test.admin.com/xxx/xxxТаким образом, страница продукта развертывается отдельно и онлайн, а затем встраивается в проект через iframe. В процессе внутреннего тестирования, поскольку среда тестирования страницы продукта и среда тестирования проекта имеют один и тот же основной домен, но разные поддомены, а страница продукта должна использовать глобальные общедоступные ресурсы, определенные в проекте, эти общедоступные ресурсы не могут быть получены из-за междоменные проблемы.
Междоменное решение для этого сценария — использование параметра document.domain. Установка document.domain для одного и того же домена на странице продукта и проекта может обеспечить междоменное взаимодействие, а вложенные страницы продукта могут получить доступ к общедоступным ресурсам родительской страницы. Следует отметить, что настройка document.domain ограничена, она может быть установлена только для самого себя или родительского домена более высокого уровня, а основной домен должен быть тем же самым.
страница проекта
<iframe src="test.admin.com/xxx/xxx"></iframe>
<script>
document.domain = 'admin.com';
</script>
Страница продукта
<script>
// 设置之后就可获取项目页面中定义的公共资源了
document.domain = 'admin.com';
</script>
6. window.name + iframe
window.nameОтносится к имени текущего окна браузера, по умолчанию это пустая строка, имя каждого окнаwindow.nameявляются независимыми. Вложенная страница iframe также имеет свой собственный объект окна, это окно является дочерним по отношению к верхнему окну и также имеет свое собственное окно.window.nameхарактеристики.
window.nameУникальность в том, что при задании страницыwindow.nameЗначение фактически эквивалентно установке имени для этого окна, а затем загрузке других страниц (даже страниц разных доменов) в этом окне,window.nameЗначение все еще там (если не сбросить, то значение не изменится), иwindow.nameЗначение поддерживает относительно большой объем памяти (2 МБ).
Например: Найдите случайную страницу, чтобы открыть консоль, и задайте имя для текущего окна.
window.name = 'test-name';
После настройки вы можете переходить на другие страницы в этом окне
window.location = 'https://www.baidu.com';
Страница переходит на домашнюю страницу Baidu, ноwindow.nameЗначение по-прежнему остается значением, установленным ранее, потому что это переход на страницу в окне, и имя окна не будет изменено.
Конкретное междоменное решение выглядит следующим образом.
http://localhost:8080/a.htmlа такжеhttp://localhost:8081/b.htmlМеждоменная связь, страница a вставляет страницу b через iframe, устанавливается на странице bwindow.nameЗначение , так как это другой домен, страница а не может напрямую получить доступ к настройкам, заданным страницей б.window.nameЗначение , требует, чтобы промежуточная страница с тем же доменом, что и страница а, использовалась в качестве моста между страницей а и страницей б.
a.html
<script>
var data = null;
var state = 0;
var iframe = document.createElement('iframe');
iframe.src = "http://localhost:8081/b.html";
iframe.style.display = 'none';
document.body.appendChild(iframe);
// 第一次加载先加载 b.html,b.html 设置好了 window.name 的值
// 而后加载 c.html,c.html 的 window.name 的值就是之前 b.html 设置的值
// 同域的情况下,a.html 可以通过 iframe.contentWindow.name 获取到 b.html 中 windoa.name 的值
iframe.onload = function() {
if(state === 0) {
iframe.src = "http://localhost:8080/c.html";
state = 1;
}else if(state === 1) {
data = iframe.contentWindow.name;
console.log('收到数据:', data);
}
}
</script>
b.html
<script>
window.name = '这是传递的数据';
</script>
Промежуточная прокси-страница должна иметь тот же домен, что и страница a, например:http://localhost:8080/c.html.
7. window.postMessage
postMessage — это новая функция HTML5 для междоменной связи между страницами.
Метод postMessage принимает два обязательных параметра:
- сообщение: данные для передачи.
- targetOrigin: доменное имя целевого окна для передачи данных, значение может быть конкретным доменным именем или подстановочным знаком «*».
a.html
<iframe src="http://localhost:8081/b.html" style='display: none;'></iframe>
<script>
window.onload = function() {
var targetOrigin = 'http://localhost:8081';
var data = {
name: '武林外传',
time: 2005,
length: 81,
address: '同福客栈'
};
// 向 b.html 发送消息
window.frames[0].postMessage(data, targetOrigin);
// 接收 b.html 发送的数据
window.addEventListener('message', function(e) {
console.log('b.html 发送来的消息:', e.data);
})
}
</script>
b.html
<script>
var targetOrigin = 'http://localhost:8080';
window.addEventListener('message', function(e) {
if(e.source != window.parent) {
return;
}
// 接收 a.html 发送的数据
console.log('a.html 发送来的消息:', e.data);
// 向 a.html 发送消息
parent.postMessage('哈哈,我是b页面,我收到你的消息了', targetOrigin);
})
</script>
Суммировать
-
Когда ресурсы с разными протоколами, доменными именами и номерами портов взаимодействуют друг с другом, возникают междоменные проблемы.
-
Из соображений безопасности политика браузера в отношении одного и того же источника ограничивает связь между разными доменами.
-
Сценарии применения междоменных решений JSONP, CORS и Server Proxy используются для обмена данными между интерфейсными и внутренними решениями.Другие междоменные решения в основном решают задачи обмена данными между страницами окна.
-
JSONP поддерживает только HTTP-запросы GET.
-
Запросы ресурсов CORS из разных источников требуют серверной поддержки.
-
Прокси-сервер напрямую позволяет серверному прокси-серверу отправлять запросы.
постскриптум
Все междоменные решения имеют соответствующие экземпляры DEMO, которые можно найти вDEMOПосмотреть в. Чтобы увидеть эффект запуска, вы можете установить модуль http-сервера глобально.
npm install -g http-server
Технический результат написан в отношении обучения и обобщения, пожалуйста, укажите на любые ошибки и проблемы в тексте. Больше технических результатов можно посмотреть в моемблог на гитхабе.
Я разобрался с некоторыми внешними учебными ресурсами, надеясь помочь тем, кто в них нуждается, по адресу:Резюме учебных ресурсов.