The same Origin policy is the cornerstone of browser security. It is designed to keep information secure and prevent malicious websites from stealing data. The so-called “homology” must satisfy the following three aspects:

  1. The agreement is the same
  2. Domain name is the same
  3. Same port (default port 80, can be omitted)

If non-homologous, the following behaviors are restricted:

  • Cookies, LocalStorageandIndexDBUnable to read
  • DOMUnable to get
  • AJAXRequest cannot be sent

Next, we will mainly explain how to solve the problems in the above three aspects.

A, Cookie,

Cookies can only be obtained by same-origin websites, but if the first level domain name of two pages is the same, but the second level domain name is different, you can set the same document.domain, and two pages can share cookies.

Many people mistakenly take WWW as a domain name, the other prefix as a secondary domain name, is wrong. The correct domain name division is:

  1. Top-level domain name:.com
  2. Level 1 domain name:baidu.com
  3. Secondary domain name:tieba.baidu.com

For example, A web site is http://w1.sillywa.com/a.html, B web is http://w2.sillywa.com/b.html, we can set up

document.domain = 'sillywa.com'
Copy the code

The cookies can then be shared between the two web pages.

Note that this method is only used for cookies and iframes. LocalStorage and IndexDB cannot circumvent the same origin policy in this way, but instead use the PostMessage API, as we’ll see below.

Second, the iframe

If two web pages have different sources, you can’t get each other’s DOM. Typical examples are iframe Windows and Windows opened with the window.open method, which cannot communicate with the parent window.

So for sites with completely different sources, there are currently three ways to avoid the same origin problem:

  • Fragment identifier (fragment identifier)
  • window.name
  • Cross document communicationAPI(window.postMessage)

1. Fragment identifier

Fragment identifier refers to the content behind the URL #, such as http://sillywa.com/a.html#fragment # of fragments, if only change the fragment identifier, not to refresh the page.

Fragment identifiers where the parent window can write information to the child window:

var src = originURL + The '#' + data
document.getElementById('myIframe').src = src
Copy the code

The child window is notified by listening for the Hashchange event:

window.onhashchange = function() {
    console.log(window.location.hash)
}
Copy the code

2.window.name

The browser window has the window.name attribute. The most important feature of this property is that, regardless of whether the same source, as long as the previous page in the same window set this property, the next page can read it.

3. window.postMessage

HTML5 has introduced a new API to solve the problem of cross-window communication: the cross-document communication API. The API adds a window.postMessage() method for Windows that allows cross-window communication, regardless of whether the two Windows are homologous. For example, assume that the parent window is http://aaa.com and the child window is http://bbb.com

Var popup = window.open(var popup = window.open('http://bbb.com'.'title');
popup.postMessage('Hello World! '.'http://bbb.com');
Copy the code

The first argument to the postMessage() method is the specific message content, and the second argument is the origin of the window that received the message, which is “protocol + domain + port”. You can also set this parameter to *, which indicates that the domain name is not limited and the message is sent to all Windows.

Similarly, a child window can send a message to a parent window by writing:

window.opener.postMessage('Nice to see you'.'http://aaa.com');
Copy the code

Both parent and child Windows can listen for messages from each other via message events:

window.addEventListener('message'.function(e) {
    console.log(e.data)
},false)
Copy the code

The Event object for the Message event has the following three properties:

  1. Event. Source:Window for sending messages
  2. Event. Origin:The url from which the message was sent
  3. Event. The data:The message content

In the following example, a child window references the parent window via the event.source property and then sends a message.

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
  event.source.postMessage('Nice to see you! '.The '*');
}
Copy the code

If we change the messages we send to LocalStorage, we can read LocalStorage to each other.

Three, AJAX

AJAX requests are also affected by the same origin policy. In addition to using proxy servers, there are several ways to achieve cross-domain implementation:

  • jsonp
  • WebScoket
  • CORS

1.jsonp

Jsonp, as you probably all know, consists of two parts: callback functions and data. The basic idea is to dynamically insert a script tag, request JSON data from the server, and the returned data will be retrieved in the callback function.

function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute("type"."text/javascript"); script.src = src; document.body.appendChild(script); } // Define the callback functionfunction foo(data) {
  console.log('Your public IP address is: ' + data.ip);
};

window.onload = function () {
  addScriptTag('http://example.com/ip?callback=foo');
}
Copy the code

The code above makes a request to server example.com by dynamically adding

2.WebScoket

Different from HTTP, WebScoket provides a two-way communication function, that is, the client can request data to the server, and the server can send data to the client. HTTP can only be one-way.

Also WebScoket uses ws:\// (unencrypted) and WSS :\// (encrypted) as protocol prefixes. This protocol does not enforce the same origin policy and can be used for cross-source communication as long as the server supports it.

To create a WebScoket, instantiate a WebScoket object and pass in the URL to connect to:

var scoket = new WebScoket("ws://www.example.com/server.php")
Copy the code

Once the WebScoket object is instantiated, the browser immediately tries to establish a connection. Like XHR, WebScoket has a set of readyState attributes that represent the current state, as follows:

  • WebScoket.OPENING(0) : The connection is being established
  • WebScoket.OPEN(1) : The connection has been established
  • WebScoket.CLOSING(2) : Closing the connection
  • WebScoket.ClOSE(3) : The connection is closed

WebScoket has no readyStatechange event; But it has other events that we’ll talk about in a minute.

To close the WebScoket connection, call the close() method:

scoket.close()
Copy the code

After the WebScoket connection, you can send and receive data. To send data, call the send() method and pass in a string, for example:

var scoket = new WebScoket("ws://www.example.com/server.php")
scoket.send('hello word')
Copy the code

Since WebScoket can only send plain text data, for complex data types we should first serialize them to JSON strings

var message = {
    name: 'sillywa'
}
scoket.send(JSON.stringify(message))
Copy the code

Again, the server must parse the data before reading it.

When the server sends a message to the client, the WebScoket object fires the Message event. This message event is similar to other messaging protocols in that the returned data is stored in the properties of event.data.

scoket.onmessage = function(event) {
    console.log(event.data)
}
Copy the code

Like the data sent to the server via send(), the data returned in Event.data is a string.

The WebScoket object has three other events that are fired at different stages of the connection life cycle.

  • Open:Triggered when a connection is successfully established
  • error: Triggered when an error occurs, the connection cannot last
  • Close:Triggered when the connection is closedWebScoketObject not supportedDOM2Level event listener, therefore must be usedDOM0The class syntax defines each event handler separately.
var scoket = new WebScoket("ws://www.example.com/server.php")
scoket.onopen = function() {
    console.log('connection start')
}
scoket.onerror = function() {
    console.log('connection error')
}
scoket.onclose = function(event) {
    console.log(event)
}
Copy the code

Of the three events, only the close event object has additional information. The event object has three additional attributes: wasClean, Code, and Reason. Where wasClean is a Boolean value indicating whether the connection has been explicitly closed; Code is the numeric status code returned by the server; Reason is a string containing information sent back by the server.

3.CORS

CORS is a W3C standard, which 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.

Whereas JSONP can only send GET requests, CORS allows any type of request to be sent. However, CORS requires both browser and server support. Currently, all browsers support Internet Explorer 10 or later.

The entire CORS communication process is automatically completed by the browser, without the user’s participation. CORS communication is no different from same-origin AJAX requests. As soon as the browser discovers that an AJAX request crosses domains, it automatically adds some header information and sometimes an additional request.

Browsers classify CORS requests into two categories: simple and non-simple.

A simple request is a simple request as long as the following two conditions are met, otherwise it is a non-simple request:

(1) The request method is one of the following:

  • HEAD
  • GET
  • POST

(2) HTTP headers do not exceed the following fields:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type: Is limited to three valuesApplication/X-www-form-urlencoded, multipart/form-data, text/plain

For simple requests, the browser automatically adds an Origin field in the header to indicate which source the request came from. The server uses this value to decide whether to approve the request or not. If Origin is not within the scope of the request, the server returns a normal HTTP response. The response does not have the access-Control-Allow-Origin field in its header, and the browser will throw an error if it finds it missing. If Origin is within the range of the request, the server returns a response with several additional header fields, one of which is access-Control-allow-Origin, which has either the Origin value or an * to indicate that requests for any domain name are allowed.

For non-simple requests, it adds an HTTP query request, called a “preflight” request, before formal communication. Usually an OPTION request. This request asks the server if the domain name of the current web page is on the server’s licensed list, and which HTTP verb and header fields can be used. The browser issues a formal XMLHttpRequest request only if it receives a positive response; otherwise, an error is reported.

If you want to know more about CORS, you can refer to the following article.

Reference article:

Ruan Yifeng “Browser Same-Origin Policy and Its Circumvention”

Ruan Yifeng, Detailed Explanation of Cross-domain Resource Sharing CORS

Reference Books:

Javascript Advanced Programming