preface

Believe each front-end for cross-domain this two word will be familiar, applied in the actual project is very much also, but cross domain methods varied let a person too many things to see, recently the company colleagues cross domain problems, and can’t find the problem, so under the summary cross domain knowledge, a distinguished word Web based operation

In fact, I began to write very early, but I didn’t understand deeply enough at the beginning of writing, and then I slowly wrote other things that I thought were taller and more profound, and then I felt that I was not perfect enough and wrote half of them, SO I fell into the cycle of obsessive-compulsive disorder patients knowing that they could not do. The cycle is long (but the articles you can see are all prepared and considered good)…

In short, it is also a backlog of articles abandoned halfway for various reasons, here finally ended, a sigh of relief, ah, or too young, end of ridicule, into the body

Isboyjc /blog portal

What is cross-domain

Simply put, cross-domain is when a document or script in one domain wants to request a resource in another domain

In fact, some resources like A link, redirect, form submission jump, like ,

What we call cross-domain is mostly a class of request scenarios that are restricted by the browser’s same origin policy, and here you might notice the same origin policy, but what is the browser’s same origin policy?

Browser Same-origin Policy

The Same Origin Policy (SOP) is a convention introduced by Netscape into the browser in 1995. It is the core and most basic security function of the browser. Without the Same Origin policy, the browser is vulnerable to XSS and CSFR attacks

Homology homology, what is a source? The source refers to the protocol, domain name, and port. The same source means the same. Even if different domain names point to the same IP address, they also have different sources

Let’s look at a domain, we in http://www.hahaha.com/abc/a.js, for example

  • http:// – > agreement
  • WWW –> subdomain name
  • Hahaha.com –> Master domain name
  • 80 –> port (http://The default port is 80)
  • ABC /a.js –> Request resource path

So let’s take the source of this domain name as an example to compare it with the following

URL The results of why
http://www.hahaha.com/abc/b.js homologous Only the path is different
http://www.hahaha.com/def/b.js homologous Only the path is different
https://www.hahaha.com/abc/a.js Different source Agreement is different
http://www.hahaha.com:8081/abc/a.js Different source Different ports
http://aaa.hahaha.com/abc/a.js Different source The host different

In the case of different sources, the same origin policy limits us

  • Storage contents such as cookies, LocalStorage, and IndexedDB cannot be read
  • DOM nodes and Js objects cannot be obtained
  • After the AJAX request is sent, the result is intercepted by the browser.

Here, I believe you have some understanding of cross-domain, so how do we effectively avoid cross-domain, should say how to solve the cross-domain problem, because we have to cross-domain in the development process, according to different types, there are many ways to solve cross-domain

Different types of cross-domain solutions

No. 1 document. Domain + iframe across domains

Introduction to the

Document. Domain to achieve cross-domain, only applicable to the main domain name is the same, the sub-domain name is different

For example, the following two pages

http://aaa.hahaha.com/a.html
http://bbb.hahaha.com/b.html
Copy the code

So what can it do

  • Both pages have the same Settingsdocument.domainAnd share the cookies
  • Both pages have the same Settingsdocument.domainThrough theiframeRealize data interchange between two pages

The sample

Share a Cookie

First, both pages are set to the same document.domain

document.domain = 'hahaha.com';
Copy the code

Page A sets a Cookie through the script

document.cookie = "test=a";
Copy the code

Page B reads this Cookie

let cookieA = document.cookie;
console.log(cookieA)
Copy the code

The server can also set the domain name of the Cookie to a level-1 domain name, such as. Hahaha.com

Set-Cookie: key=value; domain=.hahaha.com; path=/
Copy the code

In this case, the second level domain and the third level domain can read this Cookie without setting anything

Shared data
<! - a page - >
<iframe src="http://bbb.hahaha.com/b.html" onload="load()" id="frame"></iframe>
<script>
  document.domain = 'hahaha.com';
  let a = "this is a";
  
  // Get page B data
  function load(){
    let frame = document.getElementById("frame")
    console.log(frame.contentWindow.b) // this is b
  }
</script>
Copy the code
<! - b page - >
<script>
  document.domain = 'hahaha.com';
  let b = "this is b"
  
  // Get page A data
  console.log(window.parent.a); // this is a
</script>
Copy the code

limited

  • First, only if the master domain name is the same and the sub-domain name is different
  • Only applies to Cookie and IFrame Windows. LocalStorage and IndexDB data cannot be shared in this way

No.2 Location. hash + iframe Cross-domain

Introduction to the

Two pages with different sources cannot access each other’s DOM. Typical examples are iframe Windows and Windows opened by window.open methods, which cannot communicate with the parent window

For example, page A and page B from different sources, if we get each other’s data directly

Page a:http://www.hahaha0.com/a.html

<iframe src="http://www.hahaha1.com/b.html" onload="load()" id="frame"></iframe>
<script>
  let a = "this is a"
  
  // Get page B data
  function load(){
    console.log(document.getElementById("frame").contentWindow.b) 
    // Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.
  }
</script>
Copy the code

Page b:http://www.hahaha1.com/b.html

<! --b-->
<script>
  let b = "this is b"
  
  // Get page A data
  console.log(window.parent.a); / / an error
</script>
Copy the code

Obviously, you can’t get it because it’s all across domains, so document.domain, which we talked about above, can only be used with the same master domain to get around the same origin policy, and you can’t do that with different master domains

Let’s look at another method, window.location.hash, which takes the part after the hash of the URL, called the fragment identifier.

For example, http://hahaha.com/a.html#fragment’s # Fragment does not refresh the page if it simply changes the fragment identifier, as is the case with hash routing in Vue

Using location.hash + iframe we can retrieve each other’s data from different primary fields

The sample

First of all, we need to realize cross-domain communication between page A and page B. Because of different domains, iframe and location.hash are used to transmit values. However, this value is one-way, and can only be transmitted from one side to the other. So we need a middleman page C to help

Iframe location.hash is used to transfer values between different fields, and JS access is used to communicate directly between the same fields

So our logic would look like this

Different fields a and B can only communicate with each other in one direction using hash values, and different fields B and C can only communicate with each other in one direction. However, c and A are in the same domain, so C can access all objects on page A through parent-parent

Page a:http://www.hahaha0.com/a.html

<! Select * from 'a' where iframe = 'b';
<iframe id="frame" src="http://www.hahaha1.com/b.html"></iframe>
<script>
  let frame = document.getElementById('frame');

  // Pass hash values to B
  frame.src = frame.src + '# a = I am a';

  // The callback method to use for codomain C
  function cb(data) {
    console.log(data) // Print I am a+b
  }
</script>
Copy the code

Page b:http://www.hahaha1.com/b.html

<! C --> c-->
<iframe id="frame" src="http://www.hahaha0.com/c.html"></iframe>
<script>
  let frame = document.getElementById('frame');

  // Listen for the hash value from A and pass it to C.HTML
  window.onhashchange = function () {
    frame.src = frame.src + location.hash + '+b';
  };
</script>
Copy the code

Page c:http://www.hahaha0.com/c.html

<script>
  // Listen for changes in the hash value of B
  window.onhashchange = function () {
    // c calls the parent's parent to operate on the js callback of codomain A, returning the result
    window.parent.parent.cb(location.hash.replace('#a='.' '));
  };
</script>
Copy the code

No.3 window.name + iframe Cross domain

Introduction to the

The window object has a name attribute, which has the property that all pages loaded by the window share window.name during the life of a window, and each page has read and write permissions to window.name

Window.name is persistent in all pages loaded by a window and is not reset for new pages, as in the following example

A page

<script>
  window.name = 'I'm a';
  setInterval(function(){
    window.location = 'b.html'; // After two seconds, load a new page b.HTML into the current window
  },2000) 
</script>
Copy the code

Page b

<script>
  console.log(window.name); / / I am a
</script>
Copy the code

From the example above, we can see intuitively that after loading page A for 2s, it jumps to page B, and b will output “I am A” in the console

The window.name value can only be a string. The maximum value can be 2M, depending on the browser, but it is generally sufficient

So we can take advantage of this feature to cross domains, using window.name and iframe as the title suggests, so can you think of a way to circumvent cross-domains without leaving a trace?

After the above destruction, we know that if iframe is used to embed a page and b page in different domains, the data can not communicate with each other, because it will cross domains. Here, we need to use window.name + iframe to realize cross-domain data communication. Obviously we can’t load page B directly from page A by changing window.location, because what we need now is that page A doesn’t jump, but we can also get the data from page B

How do you do that? In fact, it depends on a middleman page C

First, the middleman C has to be codomain with A

Page A loads page B with iframe and leaves data in the window.name property of the current iframe window

At this time, A cannot read the iframe because of different fields, but we can dynamically change the SRC of the iframe to C in A

Middleman C doesn’t need to write anything, because it inherits window.name directly from b

Since C and A are co-domain, A can normally get the window.name attribute value in child page C

I have to say, it’s pretty impressive. Hats off to the old guard

The sample

Page a:http://www.hahaha1.com/abc/a.html

<iframe src="http://www.hahaha2.com/abc/b.html" id="frame" onload="load()"></iframe>
<script>
	let flag = true
  // The onload event fires twice
  // After the first onload succeeds, the data window.name is left, and then the local proxy page is switched
  // After the second onload page c succeeds, the data in the domain window.name is read
  function load() {
    if(flag){
      / / 1 times
      let frame = document.getElementById('frame')
      frame.src = 'http://www.hahaha1.com/abc/c.html'
      flag = false
    }else{
      / / the second time
      console.log(frame.contentWindow.name) / / I'm b}}</script>
Copy the code

Page b:http://www.hahaha2.com/abc/b.html

<script>
  window.name = 'I'm b'  
</script>
Copy the code

No. 4 window. PostMessage across domains

Introduction to the

The above cross-domain window practices are safe and secure in their respective scenarios, but they are opportunistic or, no, shortcuts, but HTML5 XMLHttpRequest Level 2 introduces a new API to address this problem: Cross-document Messaging API

The API adds a window.postMessage method to the Window object, which allows scripts from different sources to communicate asynchronously in a limited manner, enabling cross-text, multi-window, cross-domain messaging

Compatibility with major browsers is also impressive

So let’s see how it works, how does it send data

otherWindow.postMessage(message, targetOrigin, [transfer]);
Copy the code
  • otherWindow
    • A reference to a window, for exampleiframecontentWindowProperty, executewindow.openThe window object returned, either named or numerically indexedwindow.frames
  • message
    • Data to be sent to other Windows will be serialized by a structured clone algorithm, which means you can safely pass data objects to the target window without having to serialize them yourself
  • targetOrigin
    • window-passingoriginProperty to specify which Windows can receive message eventsoriginUnder the window can receive messages, set to wildcard*Indicates that you can send to any window, but this is generally not recommended for security reasons. If you want to send to a window with the same origin as the current window, set this parameter to/
  • Transfer | optional attribute
    • It is a list of andmessageSimultaneous transmissionTransferableObject, ownership of which is transferred to the receiver of the message, and ownership is no longer retained by the sender

It can also listen for message events to receive data

window.addEventListener("message", receiveMessage, false)
function receiveMessage(event) {
  let origin= event.origin
  console.log(event)
}
Copy the code

Next, we will use window.postMessage to communicate data across domains

The sample

Again, take pages A and B from different domains

Page a:http://www.hahaha1.com/abc/a.html, create cross-domain iframe and to send information

<iframe src="http://www.hahaha2.com/abc/b.html" id="frame" onload="load()"></iframe>
<script>
  function load() {
    let frame = document.getElementById('frame')
    / / send
    frame.contentWindow.postMessage('Hello, I'm a'..'http://www.hahaha2.com/abc/b.html')
    
    / / receive
    window.onmessage = function(e) {
      console.log(e.data) // Hello, this is B}}</script>
Copy the code

Page b:http://www.hahaha2.com/abc/b.html, receiving data and returns the information

<script>
  / / receive
  window.onmessage = function(e) {
    console.log(e.data) // Hello, I am A
    // Return data
    e.source.postMessage('Hello, THIS is B'., e.origin)
  }
</script>
Copy the code

No. 5 the json across domains

Writing in the front

For JSONP, although not commonly used, we will mention it well, because some beginners have encountered AJAX and JSONP confused, when mentioning JSONP, will say it is very easy, is to set the field in the AJAX request, You may have used JSONP cross-domain mode after JQuery, it is true that only add a field to the request, but that is a way of using JQ wrapped, do not be confused by the appearance, do you really understand the principle of JQ?

How AJAX works

Ajax works simply by sending asynchronous requests to the server through javascript objects called XMLHttpRequest (Ajax engine) objects in the browser and receiving the server’s response data, and then using javascript to manipulate the DOM and update the page

One of the most critical steps is to get the request data from the server, meaning that the user’s request is sent indirectly through the Ajax engine rather than directly through the browser, and the Ajax engine also receives the data returned by the server, so it doesn’t cause a full page refresh on the browser

It’s easy to use

Create an XMLHttpRequest object, that is, create an asynchronous call object. Create a new HTTP request and specify the method, URL, and authentication information of the HTTP request. Set the function that responds to changes in the STATUS of the HTTP requestCopy the code

The JSON, JSON?

JSON (JavaScript Object Notation) is a lightweight data interchange format that you can easily read at json.org

JSONP (JSON with Padding) is an unofficial protocol that allows server-side Script tags to be returned to the client and cross-domain access via javascript callback. This is a simple JSONP implementation. This might not be obvious, but how does it work

How JSONP works

Let’s start with a small example, again, of pages A and B in different fields

Page a:http://www.hahaha1.com/abc/a.html

<html>
<head>
    <title>test</title>
    <script type="text/javascript" src="http://www.hahaha2.com/abc/b.html"></script>
</head>
<body>
  <script>
  	console.log(b) / / I'm b
  </script>
</body>
</html>
Copy the code

Page b:http://www.hahaha2.com/abc/b.js

var b = "I am b"
Copy the code

As you can see, although different fields, the variables in page A can still be accessed and printed out

This small example we could see very intuitive < script > tag of the SRC attribute is not constrained by the same-origin policy, so I can get any server script and execute it, this is the core principle of the json, as to how it pass data, we have to achieve a simple

CallBack implementation of JSONP

JSONP: Javascript callback (JSONP) : javascript callback (JSONP) : javascript callback (JSONP) : javascript callback (JSONP) : javascript callback (JSONP) : javascript callback (JSONP) : javascript callback (JSONP) : javascript callback (JSONP

Page a:http://www.hahaha1.com/abc/a.html

<script type="text/javascript">
  // The callback function
  function cb(res) {
      console.log(res.data.b) / / I'm b
  }
</script>
<script type="text/javascript" src="http://www.hahaha2.com/abc/b.js"></script>
Copy the code

Page b:http://www.hahaha2.com/abc/b.js

var b = "I am b"

// Call the cb function and pass it as json data
cb({
  code:200.msg:"success".data: {b: b
  }
})
Copy the code

Creating a callback function, then calling the function on the remote service and passing the JSON data as a parameter to complete the callback, is the simple implementation pattern of JSONP, or the prototype of JSONP

Fill the JSON data into the callback function. Now you see why JSONP is called JSON with Padding

The above implementation is very simple. In general, we want the script tag to be called dynamically, rather than being fixed in THE HTML and executed directly. We can create the script tag dynamically by using javascript. In this way, we can call the remote service flexibly, so let’s simply modify page A as follows

<script type="text/javascript">
  function cb(res) {
    console.log(res.data.b)  / / I'm b
  }
  
  // Dynamically add 
  function addScriptTag(src){
    let script = document.createElement('script')
    script.setAttribute("type"."text/javascript")
    script.src = src
    document.body.appendChild(script)
  }

  window.onload = function(){
    addScriptTag("http://www.hahaha2.com/abc/b.js")}</script>
Copy the code

As shown above, the basic operations are not explained, but now we can control the execution elegantly. To call a remote service, simply add the addScriptTag method and pass in the SRC value of the remote service

Now we can happily perform a real JSONP service retrieval

We use jsonplaceholder’s Todos interface as an example, with the following interface address

https://jsonplaceholder.typicode.com/todos?callback=?
Copy the code

callback=? This spell behind the interface said the name of the callback function, also is to your own callback function is defined in the client’s function names to the server, the server will return to the way you define the callback function name, obtain the JSON data into this method to complete the callback, our callback function name is cb, then complete interface address below

https://jsonplaceholder.typicode.com/todos?callback=cb
Copy the code

So without further ado, let’s try it

<script type="text/javascript">
  function cb(res) {
    console.log(res)
  }
  
  function addScriptTag(src){
    let script = document.createElement('script')
    script.setAttribute("type"."text/javascript")
    script.src = src
    document.body.appendChild(script)
  }

  window.onload = function(){
    addScriptTag("https://jsonplaceholder.typicode.com/todos?callback=cb")}</script>
Copy the code

As you can see, after the page is loaded, the output of the interface returns the data. At this point, let’s look at the JSONP implementation in JQ

JSONP JQuery implementation

Let’s see how JQ gets the data

$.ajax({
  url:"https://jsonplaceholder.typicode.com/todos?callback=?".dataType:"jsonp".jsonpCallback:"cb".success: function(res){
    console.log(res)
  }
});
Copy the code

As you can see, the dataType field is set to JSONP in order for JQ to be accessed as JSONP. The jsonpCallback attribute is used to customize the name of our callback method, internally similar to what we wrote above

JSONP vs. AJAX

  • Call mode

    • AJAXJSONPMuch like that, you’re asking for a URL and then processing the data that the server returns
    • The classJQueryThe library is just theJSONPAs aAJAXEncapsulate a form of request, don’t get confused
  • At its core

    • AJAXThe core is throughxmlHttpRequestGet content not on this page
    • JSONPDynamic addition is at the heart ofscriptThe tag invokes the JS script provided by the server, suffix.json
  • In terms of the difference between the two,

    • AJAXCross-domain errors can be reported by different domains, but you can also use server-side proxies,CORSAnd so onJSONPThere is no restriction, it can be in the same domain or in different domains
    • JSONPIt’s a way or non-binding agreement,AJAXIt doesn’t have to bejsonFormat to pass data
    • JSONPOnly supportGETThe request,AJAXsupportGETPOST

Finally, JSONP is a very old cross-domain approach, and few people use it now, so we can just understand it, ok

In general, we want this script tag to be called dynamically, rather than being stuck in HTML and executed before the page is displayed. We can create script tags dynamically through javascript, which gives us the flexibility to call remote services

No.6 CORS Cross-domain resource sharing

What is CORS?

Before the emergence of CORS, we all use JSONP to achieve cross-domain, but this way is limited to GET requests. However, the emergence of CORS solves this problem well for us, which is also the reason why it has become a trend

CORS is a W3C standard that stands for Cross-Origin Resource Sharing.

It allows browsers to issue XMLHttpRequest requests across source servers, overcoming the limitation that AJAX can only be used in the same source

CORS needs to be supported by both the browser and the server. Currently, all browsers support this function

The entire CORS communication process is automatically completed by the browser and does not require user participation. For us developers, CORS communication is no different from same-origin AJAX communication. The code is exactly the same. Sometimes an additional request is made, but the user is unaware of the process

Therefore, the key to achieve CORS communication is the server. As long as the server sets up the allowed CORS interface, it can carry out cross-source communication. To understand how to achieve CORS cross-domain communication, we should first understand what the browser does for each request

Browsers divide CORS requests into two types: Simple request and not-so-Simple Request. Browsers handle these two types of requests differently

A simple request

What is a simple request? It’s easy to understand just remember the two, right

  • The request method isHEAD, GET, POSTOne of three ways
  • HTTP header information does not exceed the following fields
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-type (limited to three valuesapplication/x-www-form-urlencoded,multipart/form-data,text/plain)

As long as both conditions are met, the request is a simple request

For simple requests, the browser will directly send a CORS request. In the header of the request, an Origin field is automatically added to indicate the source of the request (protocol + domain name + port). Then the server will decide whether to approve the request according to the value

Non-simple request

Knowing the definition of a simple request, a non-simple request is relatively simple, because if it is not a simple request, it is a non-simple request

For non-simple requests, the browser will make a query request, called a preflight request, before the formal communication, also called an OPTIONS request, because it uses the request form OPTIONS. This request is used to ask

The browser asks the server if the domain name of the current web page is on the server’s licensed list, and what HTTP verb and header fields are available. The browser will only issue an official XMLHttpRequest request if it is, or it will report a cross-domain error

In addition to the Origin field, the access-Control-request-method field and the access-Control-request-headers field are also available. They indicate the HTTP request method used by the browser’s CORS request and the additional header fields sent by the specified browser’s CORS request, respectively. If you’re confused, don’t worry, let’s look at an example

The following is an example of an AJAX request

let url = 'http://www.hahaha.com/abc'
let xhr = new XMLHttpRequest()
xhr.open('POST', url, true)
xhr.setRequestHeader('X-Token'.'YGJHJHGJAHSGJDHGSJGJHDGSJHS')
xhr.setRequestHeader('X-Test'.'YGJHJHGJAHSGJDHGSJGJHDGSJHS')
xhr.send()
Copy the code

In this example, we send a POST request and add a custom X-Token and X-test field to its request header, which is a non-simple request because the custom request header field is added

This non-simple request would carry the following information in the precheck request header

Access-control-request-method: access-control-request-method: POST // Access-Control-request-headers: X-token, x-testCopy the code

WithCredentials attribute

CORS requests do not send cookies and HTTP authentication information by default

To send cookies to the server, the server must first agree to specify the access-Control-allow-credentials field

Access-Control-Allow-Credentials: true
Copy the code

Second, the client must turn on the withCredentials attribute in the request it initiates

xhr = new XMLHttpRequest()
xhr.withCredentials = true
Copy the code

Otherwise, either the server or client will not send or process cookies if they are not set

Although browsers do not send cookies and HTTP authentication information by default, some browsers send cookies together, and you can explicitly disable withCredentials

xhr.withCredentials = false
Copy the code

Note that the access-Control-allow-Origin field cannot be set as an asterisk to send cookies. You must specify a specific domain name that is consistent with the requested web page. Cookies still follow the same Origin policy, and only cookies set with the server domain name will be uploaded. Cookies for other domain names are not uploaded, and document. cookies in the original web code (across sources) cannot read cookies under server domain names, as discussed below

Cross-domain configuration of server CORS

The above is just to give you an idea of CORS, but it requires server configuration to solve it. The syntax of configuration items may differ from language to language, but the content is definitely the same

Configuration allows cross-domain sources

Access-Control-Allow-Origin: *
Copy the code

The access-Control-Allow-Origin field is the most critical item in CORS cross-domain request. It is a required item. It indicates the address source that the server allows cross-domain Access

Note that setting this field to * is not secure, it is recommended to specify the source, and when set to *, the browser will not send cookies, even if your XHR has set withCredentials

Configure methods to allow cross-domain requests

Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT...
Copy the code

This field is also required, and its value is a comma-separated string that indicates all methods supported by the server for cross-domain requests

Configure the allowed request header fields

Access-Control-Allow-Headers: x-requested-with,content-type...
Copy the code

This is also required if you have custom header fields in your request. It is also a comma-separated string indicating all header fields supported by the server, not just those requested by the browser in precheck

Configure whether cookies are allowed to be sent

Access-Control-Allow-Credentials: true
Copy the code

This field is optional and its value is a Boolean value indicating whether cookies are allowed to be sent. By default, cookies are not included in CORS requests

If set to true, the server explicitly approves that cookies can be included in the request and sent to the server

This field can only be set to true. If the server does not want the browser to send cookies, delete this field

The validity period of the precheck request was set

Access-Control-Max-Age: 1728000
Copy the code

In the preceding result, the validity period is 20 days (1728000 seconds), that is, the response is allowed to be cached for 20 days. During this period, if you send the interface request again, you do not need to send the precheck request, saving server resources

Common cross-domain precheck request throw errors

For our development, precheck request is the most likely to encounter obstacles in cross-domain, so enumerating several reasons for the error of precheck request, you can directly consult the backend students to find out which is wrong. As for precheck request, the ultimate goal is only one, the client sends precheck, the server allows and returns 200

OPTIONS 404

No 'access-Control-allow-origin' header is present on the requested resource and the response had HTTP status code 404Copy the code

If the server is not set to allow OPTIONS requests, the response status code will be 404 when the precheck request is made because the interface address cannot be found

Then you might need to go to the back end and gracefully tell it to allow the OPTIONS request

OPTIONS 405

No 'access-Control-allow-origin' header is present on the requested resource and the response had HTTP status code 405Copy the code

The server already allows OPTIONS requests, but some configuration files (such as security configuration) prevent OPTIONS requests

Then you might want to go to the back end and gracefully tell it to turn off the corresponding security configuration

OPTIONS 200

No 'access-Control-allow-origin' header is present on the requested resource and OPTIONS request status is 200Copy the code

The OPTIONS request was allowed on the server side and not blocked in the configuration file, but a header mismatch occurred

The so-called header matching, for example, the Origin header check does not match, or some header support is missing (such as X-requested-with, etc.), and then the server will return the Response to the front end, which will trigger xhr. onerror when the front end detects this, resulting in an error

Then you might need to go to the back end and say, gracefully, please add the corresponding header support

OPTIONS 500

This is easier. The server code for the OPTIONS request is not working, or there is no response

Then you might need to find the back end, send it a screenshot of the error message in the Network, and gracefully tell it that when it detects a precheck request, please make it 200

No.7 Nginx proxy cross domain

Iconfont is resolved across domains

Browser cross-domain access js/CSS/img conventional static resources by the same-origin policy permission, but iconfont font file such as eot | otf | the vera.ttf | woff | SVG exception, at this point in the Nginx server to add the following configuration to solve static resources

location / {
  add_header Access-Control-Allow-Origin *;
}
Copy the code

The reverse proxy interface is cross-domain

As we know, the same origin policy is only a security policy of the browser, not a part of the HTTP protocol. The server invokes the HTTP interface only using THE HTTP protocol and does not execute JS scripts. Therefore, the same origin policy is not required, so there is no crossing problem

In plain English, there is a cross-domain problem when a client browser makes a request, but there is no cross-domain problem when a server makes a request to another server, because the cross-domain problem is ultimately due to the same origin policy, which only exists in the browser

So we can configure a proxy server through Nginx, reverse proxy access cross-domain interface, and we can also modify the Cookie domain information, convenient for the current domain Cookie write

Nginx is actually a variety of configurations, easy to learn, even if you have not touched, but also easy to understand, let’s take a look at the example

First if our page a under the http://www.hahaha.com domain, but our interface under the http://www.hahaha1.com:9999 domain

Then when we make an AJAX request on page A, which is cross-domain, we can use Nginx to configure a proxy server with the same domain name as page A, both http://www.hahaha.com, to act as a springboard. The reverse proxy accesses http://www.hahaha1.com

# Nginx proxy server
server {
  listen       80;
  server_name  www.hahaha.com;

  location / {
    # Reverse proxy address
    proxy_pass   http://www.hahaha1.com:9999;  
    # change the domain name in Cookie
    proxy_cookie_domain www.hahaha1.com www.hahaha.com; 
    index  index.html index.htm;
		
    Allow-origin cannot be set to *
    add_header Access-Control-Allow-Origin http://www.hahaha.com;  
    add_header Access-Control-Allow-Credentials true; }}Copy the code

Yes, this proxy configuration can be understood without contact with Nginx, most of which we mentioned above, is not very simple

No.8 Node agent cross domain

Node implements the cross-domain proxy service in the same way as Nginx, which is used to start a proxy server. It is also used to start a proxy service in Node

Vue-various configurations of agents in the CLI

Vue-cli is based on Webpack, which uses webpack-dev-server to start scaffolding locally, that is, to start a Node service locally, to monitor and package static resources in real time, because they are packaged, only need to configure. We configure the proxy in vue.config.js as follows, which can be written in many ways

The use of a

module.exports = {
  / /...
  devServer: {
    proxy: {
      '/api': 'http://www.hahaha.com'}}}Copy the code

As shown above, when you request/API/ABC interface will be acting to http://www.hahaha.com/api/abc

Use two

Of course, if you want to delegate multiple paths to the same target, you can use the following approach

module.exports = {
  / /...
  devServer: {
    proxy: [{
      context: ['/api1'.'/api2'.'/api3'].target: 'http://www.hahaha.com',}}}Copy the code

Using three

As when we first use agent, agent/API, the final result is http://www.hahaha.com/api/abc, but sometimes we don’t want to agent/API, you can use the following this way, Path rewriting is done using the pathRewrite attribute

module.exports = {
  / /...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://www.hahaha.com'.pathRewrite: {'^/api' : ' '}
      }
    }
  }
}
Copy the code

This time, / API/ABC interface will be acting to http://www.hahaha.com/abc

The use of four

By default, our agent does not accept backend servers that run over HTTPS and use invalid certificates

If you want to accept, you need to set Secure: false as follows

module.exports = {
  / /...
  devServer: {
    proxy: {
      '/api': {
        target: 'https://www.hahaha.com'.secure: false}}}}Copy the code

Use five

Configure a changeOrigin field. When it is true, a local virtual server will receive your request and send it on your behalf, so this field is required if you want to broker across domains

module.exports = {
  // ...
  devServer: {
    proxy: {
      "/api": {
        target: 'http://www.hahaha.com'.changeOrigin: true,}}}}Copy the code

The use of six

If you want to configure multiple agents, it is easy, as shown below, to set the corresponding agent rules in any agent

module.exports = {
  // ...
  devServer: {
    proxy: {
      "/api1": {
        target: 'http://www.hahaha1.com'.changeOrigin: true
      },
      "/api2": {
        target: 'http://www.hahaha2.com'.pathRewrite: {'^/api2' : ' '}},"/api3": {
        target: 'http://www.hahaha3.com'.changeOrigin: true.pathRewrite: {'^/api3' : ' '}}// ...}}}Copy the code

When your project goes live, the front-end static file and the back-end are in the same domain. If they are not in the same domain, they will still report a cross-domain error. This time, you also need to configure the back-end cross-domain

Node implements the proxy server

We use Express + HTTP-proxy-middleware to build a proxy server. Http-proxy-middleware is used for no other reason than that it is used in Webpack-dev-server

let express = require('express')
let proxy = require('http-proxy-middleware')
let app = express()

app.use('/', proxy({
    // Proxy cross-domain target interface
    target: 'http://www.hahaha1.com:9999'.changeOrigin: true.// Modify the response header information to cross-domain and allow cookies
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin'.'http://www.hahaha.com')
        res.header('Access-Control-Allow-Credentials'.'true')},// Change the cookie domain name in the response information. If the value is false, the cookie domain name is not changed
    cookieDomainRewrite: 'www.hahaha.com'
}))

app.listen(3000)
Copy the code

No. 9 WebSocket across domains

Introduction of WebSocket

WebSocket is a protocol for full duplex communication over a single TCP connection. Born in 2008, it was standardized by IETF as RFC 6455 in 2011, and supplemented by RFC7936. WebSocket API was also standardized by W3C

WebSocket makes the data exchange between the client and the server easier, allowing the server to actively push data to the client. In the WebSocket API, the browser and the server only need to complete a handshake, they can directly create a persistent connection, and two-way data transmission. At the same time, It is also a cross-domain solution

WebSocket characteristics

  • Based on TCP protocol, the implementation of the server side is relatively easy

  • It has good compatibility with HTTP protocol, and the default ports are 80 and 443. In addition, HTTP protocol is used in the handshake phase, so it is not easy to shield the handshake and can pass various HTTP proxy servers

  • The data format is relatively light, with low performance overhead and high communication efficiency

  • You can send text or binary data

  • There are no same-origin restrictions, and clients can communicate with any server

  • The protocol identifier is WS (or WSS if encrypted), and the server URL is the URL

The following

ws://www.hahaha.com:80/abc/def
Copy the code

The sample

Each server language has support for websocket, but it is written differently. Here we use Node as an example

On the client side, we can directly use the HTML5 WebSocket API. On the server side, we can also use nodejs-webSocket to implement webSocket server, but this is not recommended, because the native WebSocket API is a bit complicated to use. The compatibility of browsers is not ideal, so we use socket. IO, which well encapsulates the webSocket interface, provides a simpler, flexible interface, but also provides backward compatibility for browsers that do not support webSocket. Using socket. IO library to achieve Websocket, when sending data can directly send serializable objects, also can customize messages, using the event string to distinguish different messages, the whole development process will be much more comfortable

To learn more about socket. IO – Portal, let’s take a look at an example

Client: http://www.hahaha.com/a.html

<script src="/socket.io/socket.io.js"></script>
<script>
  let socket = io.connect('http://www.hahaha1.com:3000')
  
  socket.on('my event', (data) => {
    console.log(data) // { hello: 'world' }
    
    socket.emit('my other event', { my: 'data'})})</script>
Copy the code

Server: http://www.hahaha1.com:3000

const app = require('express').createServer()
const io = require('socket.io')(app)

app.listen(3000)

io.on('connection', (socket) => {
  socket.emit('my event', { hello: 'world' })
  
  socket.on('my other event', (data) => {
    console.log(data) // { my: 'data' }})})Copy the code

As shown above, webSocket connection with socket. IO is super easy, so follow the documentation and try it out for yourself

The last

Welcome to pay attention to the public, “shady front end”, from time to time an article, no fancy promotion and advertising, hope can let everybody opens at random an article, you can see full of dry goods, also can add robot directly friends remark “front end after | | full stack” by automatically, and the communication group, Chatting, teasing, solving problems, making friends, of course, technology based

Line according to the time to post the summary of the whole of a few posts, there will be other petty articles, more is not posted, these articles are written about, between may have copied each other, learn from each other, these are all can’t avoid, also draw lessons from these articles written, just on my hands again, for example, with my own understanding yards down again, Spent 1 week for this spare time, with the following content have some resemblance, written by the author that is helpless, can be said to be the knowledge so much, you a summary of a slightly different place to express the grammar of the left, I also suffered, had found a tool to identify the similarity, to avoid the misunderstanding, after all, I’m particularly dislike a porter, well, it is late one night, finally call it a day, Sleep, oh right, code word is not easy, hope to praise, if there is a mistake, hope to point out, thank you for your support

Refer to the article

Browser Same Origin Policy and its Circumvention Method ruan Yifeng – 2016.04

Ruan Yifeng, 2016.04

Front-end cross-domain collation – Think of damonare – October 2016

Front-end Common Cross-domain solutions (all) – Sifu Quiet de Precipitation – July 2017

Face cross-domain correctly, don’t panic – Nuggets Neal_yang – 2017.12

Nine cross-domain implementation principles (full version) – Sailing in gold waves – January 2019

9 common front-end cross-domain solutions (in detail) – Gold Digger – July 2019