Резюме фронтенд-интервью: междоменные проблемы js

JavaScript

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

Что такое междоменный домен?

Мы часто видим ошибки, подобные приведенным выше, Access-Cotrol-Allow-Origin. Это типичная междоменная ошибка. На самом деле то, что мы обычно называем междоменным, является узким смыслом, который представляет собой тип сценария запроса, ограниченный политикой браузера в отношении одного и того же источника. Так что же такое политика того же происхождения?

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

浏览器安全的基石是"同源政策"(same-origin policy)。 Netscape 公司引入浏览器。目前,所有浏览器
都实行这个政策。
所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

Так называемый «гомологичный» относится к «трем идентичным».

协议相同域名相同端口相同

Пример:

Например,http://www.a.com:3000/index.htmlЭтот URL, протоколhttp://, доменное имяwww.a.com, порт3000(URL-адреса, на которые мы часто смотрим, не являются таковыми, потому что порт 80 по умолчанию можно не указывать).


Распространенные междоменные сценарии

URL                                      说明                    是否允许通信
http://www.a.com/a.js
http://www.a.com/b.js         同一域名,不同文件或路径           允许,属于同源
http://www.a.com/lab/c.js

http://www.a.com:8000/a.js
http://www.a.com/b.js         同一域名,不同端口                不允许
 
http://www.a.com/a.js
https://www.a.com/b.js        同一域名,不同协议                不允许
 
http://www.a.com/a.js
http://192.168.4.12/b.js      域名和域名对应相同ip              不允许
 
http://www.a.com/a.js
http://x.a.com/b.js           主域相同,子域不同                不允许
http://a.com/c.js
 
http://www.a.com/a.js
http://www.b.com/b.js         不同域名                         不允许

  • Политика того же происхождения ограничивает следующие действия:

(1) Cookie、LocalStorage 和 IndexDB 无法读取。(2) DOM 无法获得。(3) AJAX 请求不能发送。

междоменное решение

1、 通过jsonp跨域
2、CORS
3、 document.domain + iframe跨域
4、 location.hash + iframe
5、 window.name + iframe跨域
6、 postMessage跨域
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域

1. Кроссдоменный через jsonp

Обычно, чтобы снизить нагрузку на веб-сервер, мы выделяем js, css, img и другие статические ресурсы на другой сервер с независимым доменным именем, а затем загружаем статические ресурсы с разных доменных имен через соответствующие теги в html-странице. , и Разрешить браузер, основываясь на этом принципе, мы можем создать сценарий динамически, а затем запросить URL-адрес с параметрами для обеспечения междоменной связи. jsonp использует эту функцию для достижения. 

Преимущества и недостатки:

  1. JSONP — это распространенный метод для связи между серверами и клиентами из разных источников. Самая большая особенность заключается в том, что он прост и применим, поддерживаются все старые браузеры, а преобразование сервера очень мало.
  2. Можно реализовать только один запрос get,Небезопасно и уязвимо для xss-атак


Пример: при поиске в Baidu мы обнаружим, что вызовы Baidu также реализуются через jsonp. Например, в кросс-доменном поиске Baidu, режиме разработки f12, сети я могу обнаружить, что следующий показатель достигается с помощью js,


Итак, давайте смоделируем вызов этого интерфейса. https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/suwd=&json=1&p=3&sid=1460_21119_28205_28131_28266_27245_22159&req=2&bs=%E8%B7%A8%E5%9F%9F&csor=0&cb=jQuery110205514934443247432_1553744466531&_=1553744466532

Упрощено до: https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=a&cb=show Также возможно следующее изображение


  <script> 
       function show(params) {
            console.log(params);
        }

 </script>    
<script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=a&cb=show"></script>

Как показано ниже, вывод можно вызвать обычным образом:

Мок код jsonp:

function jsonp({url,params,cb}) { 
    return new Promise((resolve, reject) => { 
      let script = document.createElement('script');               
      window[cb] = function (params) {     
               resolve(params); 
      }      
      params = {...params,cb};  
      let arrs = [];  
      for(let key in params){  
          arrs.push(`${key}=${params[key]}`);       
       }                
      script.src = `${url}?${arrs.join('&')}`;   
      document.body.appendChild(script);
            });        }; 
jsonp({  
 url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', 
 params:{wd:'a'},    
 cb:'show'       
 }).then(data=>{            
 console.log('jsonp跨域请求的数据为:',data);   
});

2.)jquery ajax:

$.ajax({
    url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',
    type: 'get',
    dataType: 'jsonp',  // 请求方式为jsonp
    jsonpCallback: "show",    // 自定义回调函数名
    data: {wd:'a'}
});

3.)vue.js:

this.$http.jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', {
    params: {wd:'a'},
  jsonp: 'show'}).then((res) => {
    console.log(res); 
})


2.CORS

CORS — это стандарт W3C, полное название — «Совместное использование ресурсов из разных источников» (Совместное использование ресурсов из разных источников). Он позволяет браузерам отправлятьXMLHttpRequestзапрос, тем самым преодолевая только AJAXгомологияограничения использования.

ps: Обычный междоменный запрос: Только сервер может установить Access-Control-Allow-Origin, а фронтенд ставить не нужно.Если вы хотите запросить куки: и фронтенд и бэкенд конец должен быть установлен. Из-за ограничения политики одного и того же источника читаемый файл cookie является файлом cookie домена, в котором расположен интерфейс междоменного запроса, а не текущей страницы.

Преимущества и недостатки:

  1. В настоящее время все браузеры поддерживают эту функцию (IE8+: IE8/9 должен использовать объект XDomainRequest для поддержки CORS), и CORS также стал основным междоменным решением.
  2. Весь процесс связи CORS автоматически завершается браузером и не требует участия пользователя. Для разработчиков связь CORS ничем не отличается от связи AJAX того же происхождения, и код точно такой же. Как только браузер обнаружит, что запрос AJAX является перекрестным, он автоматически добавит некоторую дополнительную информацию в заголовок, а иногда будет сделан дополнительный запрос, но пользователь этого не почувствует.
  3. CORS служит той же цели, что и JSONP, но является более мощным, чем JSONP. JSONP поддерживает толькоGETзапросы, CORS поддерживает все типы HTTP-запросов. Преимущество JSONP в том, что он поддерживает старые браузеры и может запрашивать данные с веб-сайтов, не поддерживающих CORS.

Два вида запросов:

Браузеры делят запросы CORS на две категории: простые запросы и не очень простые запросы.

Пока следующие два условия выполняются одновременно, это простой запрос. Если следующие два условия не выполняются одновременно, это не простой запрос.

1) 请求方法是以下三种方法之一:HEADGETPOST
(2)HTTP的头信息不超出以下几种字段:
AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type:
只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

Простой запрос:

Простой запрос, браузер делает запрос CORS напрямую. В частности, в информации заголовка добавьтеOriginполе.

Непростой запрос:

Это тип запроса, который предъявляет особые требования к серверу, например, метод запросаPUTилиDELETE,илиContent-TypeТип поляapplication/json.

Для запросов CORS, которые не являются простыми запросами, перед формальным общением будет добавлен запрос HTTP-запроса, который называется предварительным запросом.

Браузер сначала запрашивает у сервера, находится ли доменное имя текущей страницы в списке разрешенных сервером и какие HTTP-команды и поля заголовка можно использовать. Только при утвердительном ответе браузер выдаст официальноеXMLHttpRequestзапроса, иначе будет сообщено об ошибке.

{ host: 'localhost:4000',
  connection: 'keep-alive',
  'content-length': '0',
  origin: 'http://localhost:3002',
  name: 'lee1',
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
  accept: '*/*',
  referer: 'http://localhost:3002/2cors.html',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'zh-CN,zh;q=0.9',
  cookie: 'name=lee' }

(1) Access-Control-Allow-Origin

Это поле обязательно к заполнению. Его значение либо при запросеOriginзначение поля, либо*, что означает, что принимаются запросы с любого доменного имени.

(2) Доступ-контроль-разрешение-учетные данные

Это поле является необязательным. Его значение является логическим значением, указывающим, разрешать ли отправку файлов cookie. По умолчанию файлы cookie не включаются в запросы CORS. установить какtrue, что означает, что сервер прямо разрешает включить файл cookie в запрос и отправить его на сервер вместе. Это значение также может быть установлено только наtrue, если сервер не хочет, чтобы браузер отправлял куки, просто удалите это поле.

(3) Access-Control-Expose-Headers

Это поле является необязательным. Когда CORS запрашивает,XMLHttpRequestобъектgetResponseHeader()Метод может получить только 6 основных полей:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma. Если вы хотите получить другие поля, вы должныAccess-Control-Expose-Headersуказано внутри. В приведенном выше примере указано, чтоgetResponseHeader('FooBar')может вернутьсяFooBarзначение поля.

свойство withCredentials

Запросы CORS не отправляют файлы cookie и HTTP-аутентификации по умолчанию. Если вы хотите отправить файлы cookie на сервер, с одной стороны, вам нужно согласие сервера, указатьAccess-Control-Allow-Credentialsполе.

Access-Control-Allow-Credentials: true

Разработчик должен открыть в запросе AJAXwithCredentialsАтрибуты.

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

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

Однако если опуститьwithCredentialsНастройки, некоторые браузеры по-прежнему будут отправлять файлы cookie вместе. В этом случае вы можете явно закрытьwithCredentials.

xhr.withCredentials = false;

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

(4) Метод запроса управления доступом

Это поле является обязательным и используется для перечисления методов HTTP, которые будут использоваться в запросе CORS браузера. Пример выше:PUT.

(5) Заголовки запроса управления доступом

Это поле представляет собой строку с разделителями-запятыми, указывающую дополнительные поля заголовка, которые будут отправляться CORS-запросами браузера.


Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

(6) Методы разрешения управления доступом

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


(7) Заголовки Access-Control-Allow

Если запрос браузера включаетAccess-Control-Request-Headersполе, тоAccess-Control-Allow-HeadersПоле, обязательное для заполнения. Это также строка с разделителями-запятыми, указывающая все поля заголовка, поддерживаемые сервером, не ограничиваясь теми, которые запрошены браузером в «предпечатной проверке».

(8) Доступ-контроль-разрешение-учетные данные

Это поле имеет то же значение, что и для простого запроса.

(9) Контроль доступа-Максимальный возраст

Это поле является необязательным и используется для указания периода действия этого запроса предварительной проверки в секундах. В приведенном выше результате период действия составляет 20 дней (1 728 000 секунд), что означает, что ответ разрешено кэшировать в течение 1 728 000 секунд (то есть 20 дней). В течение этого периода нет необходимости выдавать еще один предварительный запрос. .


моделирование:

Настройки интерфейса js;

js на странице .html

   let xhr = new XMLHttpRequest;   
 document.cookie = 'name=lee'; //cookie  不能跨域  
  xhr.withCredentials = true; //   
 xhr.open('PUT','http://localhost:4000/getData',true);  
  xhr.setRequestHeader('name', 'lee1');  
  xhr.onreadystatechange = function () {   
     if(xhr.readyState === 4){ 
           if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                console.log(xhr.response);  
          }        }            } 
 xhr.send();

Бэкенд моделируется с помощью узла:


a.js (Используется для запуска страницы по адресу http://localhost:3002/a.html, а вызов http://localhost:4000/getData относится к междоменному)

let express = require('express');
let app = express();
app.use(express.static(__dirname));app.listen(3002);

b.js

let express = require('express');
let app = express();
let whiteList = ['http://localhost:3002'];
app.use(function (req,res,next) { 
   let origin = req.headers.origin; 
   if (whiteList.includes(origin)){        // 设置哪个源可以访问
        res.setHeader('Access-Control-Allow-Origin', origin);        // 允许携带哪个头访问我 
        res.setHeader("Access-Control-Allow-Headers", "name,Content-Type,X-Requested-With");
    //    允许携带cookie       
       res.setHeader('Access-Control-Allow-Credentials','true');        // 该次请求的请求方式   
       res.setHeader("Access-Control-Allow-Methods", "PUT");        //预检测存活时间 
       res.setHeader("Access-Control-Max-Age", 1800);        // /允许前端获取哪个头 
       res.setHeader("Access-Control-Expose-Headers", "name");        // option 预检请求 又是有请求 有时没有 表示这个请求是用来询问的
        if(req.method === 'OPTIONS'){ 
           res.end();  
      }    } 
   next();})
app.put('/getData', function (req, res) {   
 console.log(req.headers);   
 // res.setHeader('name','hello lee');   
 res.end(`getData`);});app.get('/getData',function (req,res) {   
console.log(req.headers);   
 res.end(`getData`);});app.use(express.static(__dirname));
app.listen(4000);

3.document.domain + перекрестный домен iframe

Это решение ограничено сценариями междоменного приложения с одним и тем же основным доменом и разными поддоменами (доменное имя первого уровня веб-страницы одинаково, но доменное имя второго уровня отличается). Принцип реализации: Обе страницы через js принудительно устанавливают document.domain как базовый основной домен, и реализуется один и тот же домен.

1.) Родительское окно :(www.a.com/a.html)

<iframe id="iframe" src="http://child.a.com/b.html"></iframe>
<script>
    document.domain = 'a.com';
    var user = 'admin';
</script>

2.) Дочернее окно :(child.a.com/b.html)

<script>
    document.domain = 'a.com';
    // 获取父窗口中变量
    alert('get js data from parent ---> ' + window.parent.user);
</script>

4. location.hash

Принцип реализации: a и b взаимодействуют друг с другом через домены и реализуются через промежуточную страницу c (а c и a находятся в одном домене). Для трех страниц для передачи значений между разными доменами используется location.hash iframe, а для связи между одними и теми же доменами используется прямой js-доступ.

Конкретная реализация: Домен A: a.html -> Домен B: b.html -> Домен A: c.html, разные домены a и b могут обмениваться данными только в одностороннем порядке через хеш-значение, а b и c также являются разными доменами. может быть только однонаправленным Для связи, но c находится в том же домене, что и a, поэтому c может получить доступ ко всем объектам на странице через parent.parent.

1.) а.html :(www.a.com/a.html)

<iframe id="iframe" src="http://www.b.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 向b.html传hash值
    setTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);
    
    // 开放给同域c.html的回调方法
    function onCallback(res) {
        alert('data from c.html ---> ' + res);
    }
</script>

2.) б.html :(www.b.com/b.html)

<iframe id="iframe" src="http://www.a.com/c.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 监听a.html传来的hash值,再传给c.html
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    };
</script>

3.) c.html :(www.a.com/c.html)

<script>
    // 监听b.html传来的hash值
    window.onhashchange = function () {
        // 再通过操作同域a.html的js回调,将结果传回
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
    };
</script>

5.window.name + перекрестный домен iframe

В окне браузера естьwindow.nameАтрибуты. Самая большая особенность этого свойства заключается в том, что независимо от того, является ли оно тем же источником или нет, пока предыдущая веб-страница устанавливает это свойство в том же окне, последняя веб-страница может его прочитать.И может поддерживать очень длинные значения имени (2 МБ).

Родительское окно сначала открывает дочернее окно, загружает веб-страницу из другого источника, и веб-страница записывает информациюwindow.nameАтрибуты.

window.name = data;

Затем дочернее окно возвращается к URL-адресу с тем же доменом, что и основное окно.

location = 'http://parent.url.com/xxx.html';

Затем главное окно может прочитать дочернее окноwindow.name.

var data = document.getElementById('myFrame').contentWindow.name;

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

1.) а.html :(www.a.com/a.html)

var proxy = function(url, callback) {
    var state = 0;
    var iframe = document.createElement('iframe');

    // 加载跨域页面
    iframe.src = url;

    // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
    iframe.onload = function() {
        if (state === 1) {
            // 第2次onload(同域c页)成功后,读取同域window.name中数据
            callback(iframe.contentWindow.name);
            destoryFrame();

        } else if (state === 0) {
            // 第1次onload(跨域页)成功后,切换到同域代理页面
            iframe.contentWindow.location = 'http://www.a.com/c.html';
            state = 1;
        }
    };

    document.body.appendChild(iframe);

    // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)
    function destoryFrame() {
        iframe.contentWindow.document.write('');
        iframe.contentWindow.close();
        document.body.removeChild(iframe);
    }
};

// 请求跨域b页面数据
proxy('http://www.b.com/b.html', function(data){
    alert(data);
});

2.) c.html :(www.a.com/c.html
Промежуточная прокси-страница находится в том же домене, что и a.html, и ее содержимое может быть пустым.

3.) б.html :(www.b.com/b.html)

<script>
    window.name = 'This is b.html data!';
</script>

6. междоменное сообщение postMessage

Чтобы решить эту проблему, в HTML5 представлен совершенно новый API: API обмена сообщениями между документами (Cross-document messaging).

Этот APIwindowобъект добавилwindow.postMessageметод, который позволяет обмениваться данными между окнами, независимо от того, имеют ли два окна одно и то же происхождение.

postMessageПервый параметр метода — конкретное информационное наполнение, а второй параметр — источник окна, принимающего сообщение, то есть «протокол + доменное имя + порт». также можно установить на*, указывающее, что доменное имя не ограничено и отправляется во все окна.

Его можно использовать для решения следующих задач:

a.) 页面和其打开的新窗口的数据传递
b.) 多窗口之间消息传递
c.) 页面与嵌套的iframe消息传递
d.) 上面三个场景的跨域数据传递

Например, a.js b.js использует узел для включения двух интерфейсов с разными номерами портов для имитации локальной реализации междоменного взаимодействия.

Родительское окноlocalhost:4001/a.htmlв дочернее окноlocalhost:4002/b.htmlсообщение, звонокpostMessageметод подойдет. Как родительские, так и дочерние окна могут бытьmessageСобытия, слушайте сообщения друг друга.


1) а.html

 <iframe src="http://localhost:4002/b.html"     frameborder="0" id="frame" onload="load()"    ></iframe>
    <script>       
 function load(params) {
            let frame = document.getElementById('frame');
            frame.contentWindow.postMessage('从a页面向b页面','http://localhost:4002'); 
           window.onmessage = function (e) { //父窗口和子窗口都可以通过message事件,监听对方的消息。
                console.log(e.data, 'a.html');  
          }                   
 }

2)b.html

  window.onmessage = function (e) { //父窗口和子窗口都可以通过message事件,监听对方的消息。
           console.log(e.data,'b.html');
//子窗口通过event.source属性引用父窗口,然后发送消息。event.origin属性可以过滤不是发给本窗口的消息。 
          e.source.postMessage('从b向a发消息',e.origin);   
    }  

3) a.js (запустить a.js)

let express = require('express');
let app = express();
app.use(express.static(__dirname));app.listen(4001);

4) b.js (узел b.js)

let express = require('express');
let app = express();
app.use(express.static(__dirname));app.listen(4002);

messageобъект событияevent, который обеспечивает следующие три свойства.

  • event.source: окно для отправки сообщения
  • event.origin: URL для отправки сообщения
  • event.data: содержание сообщения


  • 7. междоменный прокси nginx

1. Конфигурация nginx решает вопрос междоменного использования iconfont

Междоменный доступ браузеров к обычным статическим ресурсам, таким как js, css и img, разрешен политикой одного источника, за исключением файлов шрифтов iconfont (eot|otf|ttf|woff|svg). Конфигурация может быть добавлена ​​к серверу статических ресурсов nginx.

location / {
  add_header Access-Control-Allow-Origin *;
}
2. междоменный интерфейс обратного прокси nginx

Междоменный принцип: политика одного и того же источника является политикой безопасности браузера, а не частью протокола HTTP. Вызов интерфейса HTTP на стороне сервера использует только протокол HTTP, не выполняет скрипты JS и не требует политики одного и того же источника, поэтому проблемы с перекрестным переходом не возникает.

Идея реализации: настроить прокси-сервер (имя домена такое же, как у домена1, но другие порты) через nginx в качестве трамплина, обратный прокси для доступа к интерфейсу домена2, и вы можете изменить информацию о домене в файле cookie кстати, который удобен для записи текущего доменного файла cookie и реализации междоменного входа в систему.

Конкретная конфигурация nginx:

#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

1.) Пример внешнего кода:

var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();

2.) Пример фона Nodejs:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));

    // 向前台写cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

8.WebSocket

Протокол WebSocket — это новый протокол в HTML5. Он реализует полнодуплексную связь между браузером и сервером и позволяет осуществлять междоменную связь, что является хорошей реализацией технологии push-уведомлений сервера.
WebSocket — это протокол связи, который используетws://(незашифрованное) иwss://(зашифровано) в качестве префикса протокола. Протокол не применяет политику одного и того же источника, и через него возможна связь между источниками, если сервер поддерживает это.

Родной WebSocket API не очень удобен в использовании, можно использовать Socket.io, который хорошо инкапсулирует интерфейс webSocket, предоставляет более простой и гибкий интерфейс, а также обеспечивает обратную совместимость для браузеров, не поддерживающих webSocket. Это потому, что это симуляция, в ней не установлен и не используется WebSocket.

Ниже приведен пример информации заголовка запроса WebSocket, отправленного браузером (выдержка изВикипедия).

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

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

Это потому, что естьOriginЭто поле, поэтому WebSocket не реализует политику того же происхождения. Потому что сервер может определить, разрешать ли эту связь, на основе этого поля. Если доменное имя находится в белом списке, сервер ответит следующим образом.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

1.) Интерфейсный код:

 // 可以用socket.io来兼任    let socket = new WebSocket('ws://localhost:3000')    socket.onopen = function (params) {        socket.send('前端发送信息');    }    socket.onmessage = function (e) {        console.log(e.data);    }

2.) Фон сокета Nodejs: необходимо установить модуль ws

let express = require('express');let app = express();let Websocket = require('ws');let wss = new Websocket.Server({port:3000});wss.on('connection',function (ws) {    ws.on('message',function (data) {        console.log(data);        ws.send('从服务端发送websocket信息');    })});

9. Междоменный прокси промежуточного ПО Nodejs

Промежуточное ПО ноды реализует междоменный прокси.Принцип примерно такой же, как и у nginx.Пересылка данных осуществляется путем запуска прокси-сервера.Также можно изменить доменное имя в куке в заголовке ответа, установив для параметра cookieDomainRewrite значение реализовать запись cookie текущего домена, что удобно для аутентификации входа в интерфейс.

1. Междоменный фреймворк, отличный от Vue (2 междоменных)

Используйте node + express + http-proxy-middleware для создания прокси-сервера.

1.) Пример внешнего кода:

var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问http-proxy-middleware代理服务器
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();

2.) Сервер промежуточного программного обеспечения:

var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({
    // 代理跨域目标接口
    target: 'http://www.domain2.com:8080',
    changeOrigin: true,

    // 修改响应头信息,实现跨域并允许带cookie
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
        res.header('Access-Control-Allow-Credentials', 'true');
    },

    // 修改响应信息中的cookie域名
    cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
}));

app.listen(3000);
console.log('Proxy server is listen at port 3000...');

3.) Фон Nodejs такой же (шесть: nginx)

2. Кросс-домен vue framework (один кросс-домен)

Используйте прокси-интерфейс node + webpack + webpack-dev-server для междоменного взаимодействия. В среде разработки, поскольку служба рендеринга vue и прокси-служба интерфейса являются одним и тем же webpack-dev-сервером, страница и прокси-интерфейс больше не являются междоменными, и нет необходимости устанавливать междоменную информацию в заголовке.

Частичная конфигурация webpack.config.js:

module.exports = {
    entry: {},
    module: {},
    ...
    devServer: {
        historyApiFallback: true,
        proxy: [{
            context: '/login',
            target: 'http://www.domain2.com:8080',  // 代理跨域目标接口
            changeOrigin: true,
            secure: false,  // 当代理某些https服务报错时用
            cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
        }],
        noInfo: true
    }
}

Содержание этой статьи относится к онлайн-контенту.Если есть какие-либо сходства и ошибки, пожалуйста, свяжитесь со мной.