catalogue
1 Why are there cross domain problems
2. Identify whether cross domain
3.6 cross domain CORS solution
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
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
URL | explain | Is it cross domain |
http://www.benjamin.com/a.jpg http://www.baidu.com/a.jpg | Different domain names | YES |
http://www.benjamin.com/a.jpg http://www.benjamin.com/b.jpg | Same domain name, different paths | NO |
http://www.benjamin.com:8888/a.jpg http://www.benjamin.com:9999/a.jpg | Same domain name, different ports | YES |
http://www.benjamin.com:8888/a.jpg https://www.benjamin.com:8888/a.jpg | Same domain name and port, different protocols | YES |
http://www.benjamin.com:8888/a.jpg http://192.168.12.143:8888/a.jpg | Domain name and ip corresponding to domain name | YES |
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 different | YES,COOKIE is not accessible |
http://www.benjamin.com:8888/a.jpg http://www.ben.com:8888/a.jpg | Different secondary domain names | YES |
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:
- The script of this site creates an element, and the src address points to the server that requests data across domains
- A callback function is provided to accept data, and the function name can be agreed through address parameter passing
- 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
parameter | explain |
---|---|
window/otherWindow | A 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. |
message | Data to be sent to other window s. |
targetOrigin | Specify which windows can receive message events, and its value can be * (indicating unlimited) or a URI. |
transfer | Optional. 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.