20211219_N methods to solve cross domain problems

catalogue

1 Why are there cross domain problems

2. Identify whether cross domain

3. Cross domain solutions

3.1 JSONP

3.2 PostMessage

3.3 Websocket

3.4 Nginx agent

3.5 document.domain

3.6 cross domain CORS solution

3.6. 1 what is CORS?

3.6. 2 Principle (simple request, complex request)

3.7 method of implementing WebMvcConfigurer#addCorsMappings

3.8 create a filter to solve cross domain problems

3.9 @CrossOrigin annotation

1 Why are there cross domain problems

  • SOP same origin policy: it is an agreement. The browser was introduced by Netscape in 1995. It is the most core and basic security function of the browser. Without the same origin policy, the browser is vulnerable to XSS, CSFR and other attacks. The so-called homology means that "protocol + domain name + port" are the same. Even if two different domain names point to the same ip, they are not homologous.
  • Cross origin means that the browser does not allow the source where the current page is located to request data from another source. (to understand what the source of the current page is, that is, the server path deployed on the current page) cross domain problems may not always occur. Because the cross domain problem is a security limitation of the browser for ajax requests: the ajax request initiated by a page can only be the same path as the current page domain name, which can effectively prevent cross site attacks. Therefore: cross domain issues are a limitation of ajax.  

2. Identify whether cross domain

URLexplainIs it cross domain

http://www.benjamin.com/a.jpg

http://www.baidu.com/a.jpg

Different domain namesYES

http://www.benjamin.com/a.jpg

http://www.benjamin.com/b.jpg

Same domain name, different pathsNO

http://www.benjamin.com:8888/a.jpg

http://www.benjamin.com:9999/a.jpg

Same domain name, different portsYES

http://www.benjamin.com:8888/a.jpg

https://www.benjamin.com:8888/a.jpg

Same domain name and port, different protocolsYES

http://www.benjamin.com:8888/a.jpg

http://192.168.12.143:8888/a.jpg

Domain name and ip corresponding to domain nameYES

http://www.benjamin.com:8888/a.jpg

http://pm.benjamin.com:8888/a.jpg

The primary domain name is the same, but the subdomain name is differentYES,COOKIE is not accessible

http://www.benjamin.com:8888/a.jpg

http://www.ben.com:8888/a.jpg

Different secondary domain namesYES

 

 

Domain name division:

3. Cross domain solutions

3.1 JSONP

The earliest solution is based on the principle that script tags can be implemented across domains
Limitations:

  • Service support required
  • Only GET requests can be initiated

Steps:

  1. The script of this site creates an element, and the src address points to the server that requests data across domains
  2. A callback function is provided to accept data, and the function name can be agreed through address parameter passing
  3. After receiving the request, the server returns a response string wrapped with JSON data, similar to this: callback({...})

After receiving the response, the browser will execute the callback function callback and pass the parsed JSON object as a parameter, so that we can process the data in the callback. In actual development, when the callback function name is the same, you can simply encapsulate a JSONP function:

function jsonp({ url, params, callback }) {  
  return new Promise((resolve, reject) => {  
    // Create a temporary script tag to initiate the request  
    const script = document.createElement('script');  
    // Temporarily bind the callback function to the window object. After the execution of the callback function, remove the script tag  
    window[callback] = data => {  
      resolve(data);  
      document.body.removeChild(script);  
    };  
    // Construct GET request parameters, key = value & callback = callback  
    const formatParams = { ...params, callback };  
    const requestParams = Object.keys(formatParams)  
      .reduce((acc, cur) => {  
        return acc.concat([`${cur}=${formatParams[cur]}`]);  
      }, [])  
   .join('&');  
 // Construct the url address of the GET request  
    const src = `${url}?${requestParams}`;  
    script.setAttribute('src', src);  
    document.body.appendChild(script);  
  });  
}  
  
// When called  
jsonp({  
  url: 'https://xxx.xxx',  
  params: {...},  
  callback: 'func',  
})  

We encapsulated the request with Promise to make the asynchronous callback more elegant. However, despite the large paragraph written in the upstairs, it is essentially:

<script src='https://xxx.xxx.xx?key=value&callback=xxx'><script> 

3.2 PostMessage

PostMessage is an API in Html5 XMLHttpRequest Level 2. It can realize cross document messaging. In terms of compatibility, IE8 +, Chrome, Firfox and other mainstream browsers support it, so you can use it safely 😊, How to understand cross document communication? You can compare the publish subscribe mode in the design mode. Here, one window sends messages and the other window receives messages. The reason why it is similar to the publish subscribe mode rather than the observer mode is that there is no direct communication between the two windows, but through the browser, a third-party platform.

send out:

window.postMessage(message, origin, [transfer])

The postMessage method has three parameters: the message to be sent, the source of the message to be received, and an optional Transferable object

parameterexplain
window/otherWindowA reference to other windows, such as the contentWindow property of iframe and the execution window The window object returned by open, or a named or numerically indexed window frames.
messageData to be sent to other window s.
targetOriginSpecify which windows can receive message events, and its value can be * (indicating unlimited) or a URI.
transferOptional. It is a string of Transferable objects that are passed simultaneously with message. Ownership of these objects will be transferred to the receiver of the message, and the sender will no longer retain ownership.

Receive: a listener is required

window.addEventListener("message", function receiveMessage(event) {}, false); // Recommended, better compatibility  
  
window.onmessage = function receiveMessage(event) {} // Not recommended. This is an experimental function, and the compatibility is not as good as the above method  

After receiving the message, the message object event contains three attributes: source, origin and data, where data is the message we sent. In addition, in addition to window communication, postMessage can also communicate with web workers and Service Work.

  • event.source – message source, message sending window / iframe.
  • event.origin – the URI of the message source (which may contain protocol, domain name and port) to validate the data source.
  • event.data – data sent.

3.3 Websocket

Websocket is a persistent protocol of HTML5. It realizes the full duplex communication between browser and server. At the same time, it is also a cross domain solution. What is full duplex communication? Simply put, after the connection is established, both server and client can actively send or receive data to each other. The native WebSocket API is inconvenient to use. We usually choose to encapsulate a websocket or use an existing third-party library. Here, we take the third-party library ws as an example:

const WebSocket = require('ws');  
  
const ws = new WebSocket('ws://www.host.com/path');  
  
ws.on('open', function open() {  
  ws.send('something');  
});  
  
ws.on('message', function incoming(data) {  
  console.log(data);  
});  
... ...  

It should be noted that WebSockets are long connections. Establishing multiple Websocket connections on a page may cause performance problems.

3.4 Nginx agent

Let's take a look at the cross domain nginx configuration file: at this time, the source of the page is manage leyou. COM: 80, and the source of back-end code service is API leyou. COM: 80. Domain names are different. The ip and port corresponding to domain names and protocols are the same. They also belong to cross domains.

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  manage.leyou.com;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

	#Front page
        location / {
	    proxy_pass http://127.0.0.1:9001;
            #root   html;
            #index  index.html index.htm;
        }
		
   
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }	
		
    }
	
	server {
		listen 80;
		server_name api.leyou.com;
		
		proxy_set_header X-Forward-Host $host;
		proxy_set_header X-Forward-Server $host;
		proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
		
		#gateway
		location /api {
			proxy_pass http://127.0.0.1:10010;
			proxy_connect_timeout 600;
			proxy_read_timeout 600;
		}
		
	
	
	}
	

    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

You can configure the back-end service configuration and the source of the page together and distinguish them according to different paths. The path for ajax to access the background code is also manage leyou. COM: 80 / API / * * * in this way, it belongs to the same source as the page and does not belong to cross domain, but the path is different and the protocol port domain name is the same.

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  manage.leyou.com;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

		#Front page
        location / {
			proxy_pass http://127.0.0.1:9001;
            #root   html;
            #index  index.html index.htm;
        }
		
		
		#gateway
		location /api {
			proxy_pass http://127.0.0.1:10010;
			proxy_connect_timeout 600;
			proxy_read_timeout 600;
		}
		
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
		
    }

    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

3.5 document.domain

If the secondary domain name is the same, set document Domain can realize cross domain. How to implement cross domain?

document.domain = 'test.com' // Setting domain is the same  
  
// Embedding cross domain pages through iframe  
const iframe = document.createElement('iframe')  
iframe.setAttribute('src', 'b.test.com/xxx.html')  
iframe.onload = function() {  
  // After you get the iframe instance, you can directly access the data in the iframe  
  console.log(iframe.contentWindow.xxx)  
}  
document.appendChild(iframe)  

3.6 cross domain CORS solution

3.6. 1 what is CORS?

CORS is a W3C standard, whose full name is "cross origin resource sharing". It allows browsers to send XMLHttpRequest requests to cross source servers, thus overcoming the limitation that AJAX can only be used in the same source.

CORS requires both browser and server support. At present, all browsers support this function, and IE browser cannot be lower than IE10

1) Browser side

At present, all browsers support this function (not below IE10). The whole CORS communication process is completed automatically by the browser without user participation.

2) Server

CORS communication is no different from AJAX, so you don't need to change the previous business logic. However, the browser will carry some header information in the request. We need to judge whether it is allowed to cross domains, and then add some information to the response header. This is usually done through filters.

3.6. 2 Principle (simple request, complex request)

The browser will divide ajax requests into two categories, and their processing schemes are slightly different: simple requests and special requests. Don't think that simple requests must not cross domains, and complex requests must cross domains. Understand what cross domains? Simple requests may also cross domains, and complex requests may not cross domains, cross domains or not cross domains to see whether they are homologous.

3.6. 2.1 simple request

What is a simple request?

(1) The request method is one of the following three methods:
    HEAD, GET,POST
(2) The header information of HTTP does not exceed the following fields:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
Content type: limited to three values: application/x-www-form-urlencoded, multipart / form data, and text/plain

When the browser finds that the initiated ajax request is a simple request, it will carry a field in the request header: Origin
Origin will indicate which domain the current request belongs to (protocol + domain name + port) and compare it with the Request URL. The service will decide whether to allow it to cross domains according to this value.

If the server allows cross domain, the following information needs to be carried in the returned response header:
    Access-Control-Allow-Origin: http://manage.leyou.com
    Access-Control-Allow-Credentials: true
    Content-Type: text/html; charset=utf-8
Access control allow origin: an acceptable domain, which is a specific domain name or * (representing any domain name)
Access control allow credentials: whether cookies are allowed to be carried. By default, cors will not carry cookies unless this value is true.
To manipulate cookie s, three conditions need to be met:

  • The response header of the service needs to carry access control allow credentials and be true.
  • The browser needs to specify withCredentials as true to initiate ajax
  • The access control allow origin in the response header must not be *, it must be the specified domain name

3.6. 2.2 complex requests

Preflight: a special request will add an HTTP query request before formal communication, which is called a "preflight" request.

The browser first asks the server whether the domain name of the current web page is in the license list of the server, and what HTTP verbs and header information fields can be used. Only when you get a positive reply will the browser send a formal XMLHttpRequest request, otherwise an error will be reported.

A template for a "pre check" request:

Compared with simple requests, there are two more request headers besides Origin:

Access control request method: the next request method, such as PUT

Access control request headers: header information for additional use

Response to pre inspection request:

 

If the service allows cross domain, in addition to access control allow origin and access control allow credentials, there are three additional headers:

Access control allow methods: access allowed methods;

Access control allow headers: allowed headers;

Access control Max age: the valid duration of this license is in seconds. ajax requests before expiration do not need to be pre checked again.

If the browser receives the above response, it is considered that it can cross domain, and the subsequent processing is the same as that of a simple request.

Re inject CorsFilter to solve cross domain problems:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * Cross domain: the browser does not allow the source where the current page is located to request data from another source. (to understand what the current page source is?)
 * SOP same origin policy: it is an agreement, which was introduced by Netscape in 1995,
 * It is the core and basic security function of the browser. Without the homology policy, the browser is vulnerable to XSS and CSFR
 * Wait for an attack. The so-called homology means that "protocol + domain name + port" are the same. Even if two different domain names point to the same ip, they are not homologous.
 */
@Configuration
public class LeyouCorsConfiguration {

    @Bean
    public CorsFilter corsFilter() {

        // Initialize the cros configuration object
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // Cross domain domain names are allowed. If you want to carry cookie s, you can't write *. *: Represents that all domain names can be accessed across domains
        // You can add multiple domain names (source: the source where your page is located)
        corsConfiguration.addAllowedOrigin("http://manage.leyou.com");

        // Allow cookie s
        corsConfiguration.setAllowCredentials(true);

        // Cross domain request methods are allowed, * represents all methods. You can add more than one
        corsConfiguration.addAllowedMethod("*");

        // Header information allowed to be carried across domains, * represents all headers. You can add more than one
        corsConfiguration.addAllowedHeader("*");

        // The valid time of this license is in seconds. ajax requests before expiration do not need to be pre checked again
        // The default is 1800s, and 1h is set here
        corsConfiguration.setMaxAge(3600L);

        // Initialize cors configuration source object
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        // Intercept all paths
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);

        // Return to the new CorsFilter
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

3.7 method of implementing WebMvcConfigurer#addCorsMappings

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // Intercepted path
        registry.addMapping("/**")
                // Allow cross domain domain domain names, *: represents all. Cookies allowed cannot be empty*
                .allowedOrigins("*")
                // Allow cross domain request methods
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                // Whether to allow cookie s to be carried across domains. If true, the domain name that allows cross domains needs to be specified and cannot be null*
                .allowCredentials(false)
                // The valid time of this license is in seconds. ajax requests before expiration do not need to be pre checked again
                // The default is 1800s, and 1h is set here
                .maxAge(3600)
                // Header information allowed to be carried across domains, * represents all headers. You can add more than one
                .allowedHeaders("*");
    }
}

3.8 create a filter to solve cross domain problems

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@WebFilter(urlPatterns = { "/*" }, filterName = "headerFilter")
public class HeaderFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(HeaderFilter.class);

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
       HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse;
        // Resolve cross domain access errors
        // Allow cross domain domain names, *: represents all domain names
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
        // Methods to allow cross domain requests
        httpServletResponse.setHeader("Access-Control-Allow-Methods",  "POST, PUT, GET, OPTIONS, DELETE");
        // The valid time of this license is in seconds. ajax requests before expiration do not need to be pre checked again
        // The default is 1800s, and 1h is set here
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        // Allowed response headers
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, client_id, uuid, Authorization");
        // Support HTTP 1.1
        httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        // Support HTTP 1.0 response. setHeader("Expires", "0");
        httpServletResponse.setHeader("Pragma", "no-cache");
        // code
        httpServletResponse.setCharacterEncoding("UTF-8");
        // Release
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("-----------------cross origin filter start-------------------");
    }

    @Override
    public void destroy() {
        logger.info("-----------------cross origin filter end-------------------");
    }
}

3.9 @CrossOrigin annotation

@CorssOrigin is an annotation that can be easily solved and can be used on classes and methods.

Keywords: Java Front-end Spring Boot Back-end

Added by skyriders on Sun, 19 Dec 2021 18:28:08 +0200