Междоменная практика двух или трех вещей

HTTP

Чтобы обеспечить актуальность и точность контента, вы можетеПосмотреть исходный текст

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

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

Причина, по которой существуют междоменные решения, заключается в ограничениях политики одного и того же источника. Политика одного и того же происхождения предусматривает, что если протокол, доменное имя и порт двух URL-адресов не совпадают, они считаются перекрестными. Например, в следующей таблице перечислены иhttp://127.0.0.1:3000Сравните результаты анализа гомологии,

url результат причина
http://127.0.0.1:3000/index гомология
https://127.0.0.1:3000 перекрестное происхождение разные протоколы
https://localhost:3000 перекрестное происхождение разные доменные имена
http://127.0.0.1:3001 перекрестное происхождение разные порты

Каковы последствия перекрестного происхождения? Есть три обобщения: Cookie, LocalStorage, IndexedDB не могут быть получены; узлы dom не могут быть получены; общая Ajax-связь не может быть выполнена; появление междоменных решений должно решить вышеуказанные болевые точки.

кросс-доменный JSONP

Когда дело доходит до междоменного JSONP, я должен упомянуть об этом в первую очередь.scriptэтикетки иimg,iframeПодобно тегам, эти теги не ограничены политикой одного и того же происхождения.Основой JSONP является выполнение запроса целевого URL-адреса путем динамической загрузки тегов сценария.

Давайте сначала посмотрим на вызов JSONPHeadersраздел, поля следующие:

Request URL:http://127.0.0.1:3000/?callback=handleResponse
Request Method:GET
Status Code:200 OK
Remote Address:127.0.0.1:3000

можно четко найти вRequest URLесть предложение?callback=handleResponse, handleResponse, за которым следует этот обратный вызов, является именем функции обратного вызова (которое может быть взято произвольно), сервер получит этот параметр, а затем объединит его в форму, подобнуюhandleResponse(JSON)Верните его во внешний интерфейс в виде JSONP == JSON с отступом, как показано на рисунке ниже.В это время браузер автоматически вызовет нашу предопределенную функцию handleResponse.

Пример внешнего кода: (источникhttp://127.0.0.1:3001)

function handleResponse(res) {
  console.log(res) // {text: "jsonp"}
}

const script = document.createElement('script')
script.src = 'http://127.0.0.1:3000?callback=handleResponse'
document.head.appendChild(script)

Пример кода на стороне сервера: (Источникhttp://127.0.0.1:3000)

const server = http.createServer((req, res) => {
  if (~req.url.indexOf('?callback')) { // 简单处理 JSONP 跨域的时候
    const obj = {
      "text": 'jsonp',
    }
    const callback = req.url.split('callback=')[1]
    const json = JSON.stringify(obj)
    const build = callback + `(${json})`
    res.end(build) // 这里返还给前端的是拼接好的 JSON 对象
  }
});

Можно видеть, что JSONP имеет преимущество в прямом доступе к тексту ответа, но нелегко подтвердить, не выполняется ли запрос JSONP, потому что событие onerror тега script широко не поддерживается браузерами, и оно может поддерживать только Вызов метода GET.

Перекрестный домен CORS

CORS (Cross-Origin Resource Sharing) можно понимать как расширенную версию Ajax, а также текущее широко распространенное междоменное решение. Его основная идея заключается в前端与后端进行 Ajax 通信时,通过自定义 HTTP 头部设置从而决定请求或响应是否生效.

Например, интерфейсный код (urlhttp://127.0.0.1:3001) написал сегмент Ajax, код такой:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
      console.log('responseTesx:' + xhr.responseText)
    }
  }
}
xhr.open('get', 'http://127.0.0.1:3000', true)
xhr.send()

Из-за несоответствия портов в это время вызываются разные источники, в это время такая строка полей будет найдена в заголовках запроса.

Origin: http://127.0.0.1:3001

И в консоли появится сообщение об ошибке:

Failed to load http://127.0.0.1:3000/: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:3001' is therefore not allowed access.

В это время вам нужно установить поле на стороне сервераAccess-Control-Allow-Origin, его роль заключается в том, чтобы установить запрос из какого источника разрешен, если установлено значение*, указывающий, что разрешены запросы из произвольных источников. Пример кода сервера выглядит следующим образом:

http.createServer((req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:3001') // 设置允许来自 http://127.0.0.1:3001 源的请求
})

CORS делится на простые запросы и непростые запросы. Это можно отличить так, если метод запросаPOST,GET,HEADэто простой запрос, другие методы, такие какPUT,DELETEПодождите не простой запрос, если это не простой запрос, вы можете увидеть его еще раз в Сети ChromeRequest MethodдляOPTIONSзапрос. Как показано ниже:

Этот запрос можно назвать предварительным запросом, в переводе на просторечие браузер спрашивает сервер, 'серверный брат, я хочу сделать запрос PUT на этот раз, пожалуйста, пришлите мне пропуск', серверный брат видит, что браузерный брат такой внимательный, что дал ему пропуск под названиемAccess-Control-Allow-Methods:PUT, и браузер может с радостью выполнять запросы PUT. Пример кода сервера выглядит следующим образом:

http.createServer((req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:3001')
  res.setHeader('Access-Control-Allow-Methods', 'http://127.0.0.1:3001')
})

Поговорив о разнице между простым запросом и непростым запросом, давайте посмотрим, как использовать CORS для реализации междоменной передачи файлов cookie.Во-первых, установите значение файла cookie на сервере и отправьте его в браузер. .Если это не междоменное, то браузер снова при запросе к серверу принесет куки, предоставленные сервером, а как насчет междоменного? Не продавай, нужно поставить на серверAccess-Control-Allow-Credentialsполя и настройки на стороне клиентаwithCredentialsПоле, оба незаменимы, код такой:

Пример внешнего кода: (источникhttp://127.0.0.1:3001)

const xhr = new XMLHttpRequest()
...
xhr.withCredentials = true // 传 cookie 的时候前端要做的
xhr.open('get', 'http://127.0.0.1:3000', true)
xhr.send()

Пример кода на стороне сервера: (источникhttp://127.0.0.1:3000)

const server = http.createServer((req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:3001') // 必填:接受域的请求
  res.setHeader('Set-Cookie', ['type=muyy']) // 下发 cookie
  res.setHeader('Access-Control-Allow-Credentials', true) // ② 选填:是否允许浏览器传 cookie 到服务端,只能设置为 true
  res.end('date from cors')
})

На данный момент было представлено практическое применение нескольких ключевых HTTP-заголовков в CORS.Для получения более подробной информации см.Cross-Origin Resource Sharing, и, наконец, суммируйте преимущества и недостатки CORS. Преимущество заключается в том, что он поддерживает все типы методов HTTP. Недостатком является то, что некоторые старые браузеры не поддерживают CORS.

hash + iframe

В начале статьи было упомянуто, что тег iframe также является одним из тегов, которые не ограничены политикой одного и того же происхождения.Основная междоменная идея хэш + iframe заключается в динамическом изменении значения хеша. src тега iframe в источнике A, в источнике B переданоwindow.onhashchangeдля захвата соответствующего хеш-значения. По идее не сложно перейти сразу к коду:

Пример кода страницы (источникhttp://127.0.0.1:3000)

<body>
  <iframe src="http://127.0.0.1:3001"></iframe>
  <script>
    const iframe = document.getElementsByTagName('iframe')[0]
    iframe.setAttribute('style', 'display: none')
    const obj = {
      data: 'hash'
    }
    iframe.src = iframe.src + '#' + JSON.stringify(obj) // ① 关键语句
  </script>
</body>

Пример кода страницы B (источникhttp://127.0.0.1:3001)

window.onhashchange = function() { // ① 关键语句
  console.log('来自 page2 的代码 ' + window.location.hash) // 来自 page2 的代码 #{"data":"hash"}
}

Обновив страницу A, вы можете обнаружить, что следующие поля печатаются на консоли, пока что реализован междоменный доступ.

来自 page2 的代码 #{"data":"hash"}

Преимущество междоменной связи таким образом заключается в том, что она поддерживает связь между страницами, а недостатком является то, что она поддерживает только метод GET и одностороннюю междоменную связь.

postMessage

Чтобы добиться обмена сообщениями между документами (обмена сообщениями между документами), именуемого XDM. HTML5 дает api - postMessage, метод postMessage() получает два параметра:发送消息так же как消息接收方所在域的字符串. Пример кода выглядит следующим образом:

Пример кода страницы (источникhttp://127.0.0.1:3000)

<body>
  <iframe src="http://127.0.0.1:3001"></iframe>
  <script>
    const iframe = document.getElementsByTagName('iframe')[0]
    iframe.setAttribute('style', 'display: none')
    iframe.onload = function() { // 此处要等 iframe 加载完毕,后续代码才会生效
      iframe.contentWindow.postMessage('a secret', 'http://127.0.0.1:3001')
    }
  </script>
</body>

Пример кода страницы B (источникhttp://127.0.0.1:3001)

window.addEventListener('message', function(event) {
  console.log('From page1 ' + event.data)
  console.log('From page1 ' + event.origin)
}, false)

Обновив страницу A, вы можете обнаружить, что следующие поля печатаются на консоли, пока что реализован междоменный доступ.

From page1 a secret
From page1 http://127.0.0.1:3000

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

WebSockets

WebSockets — это протокол HTML5, цель которого — установить полнодуплексную связь через постоянное соединение. Поскольку WebSockets принимает собственный протокол, преимущество заключается в том, что клиент и сервер отправляют меньше данных, а недостатком является то, что требуется дополнительный сервер. Основное использование заключается в следующем:

const ws = new WebSocket('ws://127.0.0.1:3000')
ws.onopen = function() {
  // 连接成功建立
}

ws.onmessage = function(event) {
  // 处理数据
}

ws.onerror = function() {
  // 发生错误时触发,连接中断
}

ws.onclose = function() {
  // 连接关闭时触发
}

Конечно, мы обычно используем сторонние библиотеки, которые инкапсулируют WebSockets.socket.io, который здесь не подробно описан.

адрес проекта

Демонстрации пяти междоменных практик, описанных выше, были загружены наcross-domain, интерфейсная среда построена на основе приложения create-react-app, а внутренняя среда построена на основе node.

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