Solve cross-domain problems in SpringBoot and Vue respectively

1. Why cross-domain problems occur

Out of the browser's same-origin policy.

The same-origin policy is a convention. It is the core and most basic security function of the browser. If the same-origin policy is missing, the normal functions of the browser may be affected. It can be said that the web is built on the basis of the same-origin policy, and the browser is only an implementation of the same-origin policy. The same-origin policy prevents the JavaScript scripts of one domain from interacting with the content of another domain. The so-called homology (that is, in the same domain) means that two pages have the same protocol, host and port number port.

2. When any one of the protocol, domain name, and port of a request url is different from the current page url, it is cross-domain

Current page url Requested page url Cross-domain the reason
http://www.test.com/ http://www.test.com/index.html no Same source (the same protocol, domain name, and port number)
http://www.test.com/ https://www.test.com/index.html Cross-domain Different protocols (http/https)
http://www.test.com/ http://www.baidu.com/ Cross-domain The main domain name is different (test/baidu)
http://www.test.com/ http://blog.test.com/ Cross-domain Different subdomains (www/blog)
http://www.test.com:8080/ http://www.test.com:7001/ Cross-domain The port number is different (8080/7001)

Three, non-homologous restriction

  1. Unable to read cookies, localstorage and indexedDB of non-same-origin web pages
  2. Cannot access the DOM of non-same-origin web pages
  3. Cannot send AJAX request to non-same source address
  4. Unable to make controller interface call

Four, five ways to solve cross-domain problems

1. @Configuration in SpringBoot solves cross-domain (recommended)

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;  
  
@Configuration  
public class CorsConfig {
    @Bean  
    public CorsFilter corsFilter() {  
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
        source.registerCorsConfiguration("/**", buildConfig()); 
        return new CorsFilter(source);  
    }  
    
   private CorsConfiguration buildConfig() {  
        CorsConfiguration corsConfiguration = new CorsConfiguration();  
        // 1允许任何域名使用
        corsConfiguration.addAllowedOrigin("*"); 
        // 2允许任何头
        corsConfiguration.addAllowedHeader("*"); 
         // 3允许任何方法(post、get等) 
        corsConfiguration.addAllowedMethod("*");
        return corsConfiguration;  
    }  
}

2. Handwriting filter

package com.ninesword.utils;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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

@Component
public class CrossFilter implements Filter {
    private static Logger logger = LoggerFactory.getLogger(CrossFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void destroy() {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        logger.debug("跨域请求进来了。。。");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        httpServletRequest.getSession();
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpResponse.setHeader("Access-Control-Allow-Methods", "*");
        httpResponse.setHeader("Access-Control-Max-Age", "3600");
        httpResponse.setHeader("Access-Control-Allow-Headers",
                "Origin, X-Requested-With, Content-Type, Accept, Connection, User-Agent, Cookie");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Content-type", "application/json");
        httpResponse.setHeader("Cache-Control", "no-cache, must-revalidate");
        chain.doFilter(request, httpResponse);
    }
}

Configure filters and interfaces to be filtered in web.xml

<!-- 添加过滤器过滤跨域请求 -->
 <filter>
   <filter-name>cors</filter-name>
   <!-- 这里配置上面刚刚设置的java过滤器文件 -->
   <filter-class>com.ninesword.utils.CrossFilter</filter-class>
 </filter>
 <filter-mapping>
   <filter-name>cors</filter-name>
   <!-- 这里配置你需要进行跨域的接口,*代表jsForSdp当前路径下所子有路径 -->
   <url-pattern>/jsForSdp/*</url-pattern>
 </filter-mapping>

After the above configuration, you can successfully cross-domain.

3. Use Spring interceptor to solve cross-domain problems

package com.test.test.conf;

import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;

//拦截器添加跨域支持(如果是web.xml配置拦截器,请将@component删除)
@Component
public class CORSFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

in web.xml

<!--解决跨域访问-->
<filter>
    <filter-name>crossorigin</filter-name>
    <filter-class>com.test.test.crossorigin</filter-class>
</filter>
<filter-mapping>
    <filter-name>crossorigin</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4. JSONP implementation (only applicable to GET requests, not recommended, please refer to the cross-domain solution in the six front-end for details)

5. Annotation implementation

(1) Since Spring MVC 4.2, support for cross-domain access has been added.

Add @CrossOrigin annotations to methods or classes.

The 2 parameters in @CrossOrigin:

  1. origins: List of domains that are allowed to be accessed
  2.  maxAge: The maximum age (in seconds) of the cache duration of the response before the flight.

(2) Custom rules support global cross-domain access

Configure the mapping path in the spring-mvc.xml file as follows:

<mvc:cors>  
    <mvc:mapping path="/cross/*"/>  
</mvc:cors> 

If all methods of the entire project are accessible, you can configure it like this

<mvc:cors>    
    <mvc:mapping path="/**"/>    
</mvc:cors> 

Where * means match to the next layer

** means that no matter how many layers there are, it can be matched.

6, nginx implementation

Five, the cross-domain solution in the front end

1. Set document.domain to solve the problem of not being able to read cookies from non-same-origin web pages

 Because the browser checks whether two web pages are of the same origin through the document.domain property, the two web pages can share cookies as long as the same document.domain is set (this scheme is limited to cross-domains with the same primary domain and different subdomains Application scenarios.)


// 两个页面都设置
document.domain = 'test.com';

2. Cross-document communication API: window.postMessage()

Call the postMessage method to implement the parent window http://test1.com to send a message to the child window http://test2.com (the child window can also send messages to the parent window through this method)

It can be used to solve the following problems:

  1. Data transfer between pages and new windows opened
  2. Message passing between multiple windows
  3. Page and nested iframe messaging
  4. Cross-domain data transfer in the above three scenarios
// 父窗口打开一个子窗口
var openWindow = window.open('http://test2.com', 'title');
 
// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
openWindow.postMessage('Nice to meet you!', 'http://test2.com');

Call the message event to monitor the message sent by the other party

// 监听 message 消息
window.addEventListener('message', function (e) {
  console.log(e.source); // e.source 发送消息的窗口
  console.log(e.origin); // e.origin 消息发向的网址
  console.log(e.data);   // e.data   发送的消息
},false);

3 、 JSONP

JSONP is a common method for cross-origin communication between server and client.

The biggest feature is simplicity and good compatibility. The disadvantage is that it only supports get requests, not post requests.

The core idea: the web page requests JSON data from the server by adding a <script> element. After the server receives the request, it sends the data back in the parameter position of a callback function with a specified name.

① Native implementation

<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
 
// 处理服务器返回回调函数的数据
<script type="text/javascript">
    function dosomething(res){
        // 处理获得的数据
        console.log(res.data)
    }
</script>

②jQuery ajax

$.ajax({
    url: 'http://www.test.com:8080/login',
    type: 'get',
    dataType: 'jsonp',  // 请求方式为jsonp
    jsonpCallback: "handleCallback",    // 自定义回调函数名
    data: {}
});

③Vue.js

this.$http.jsonp('http://www.domain2.com:8080/login', {
    params: {},
    jsonp: 'handleCallback'
}).then((res) => {
    console.log(res); 
})

4 、 CORS

CORS is short for cross-domain resource sharing. It is a W3C standard and is a fundamental solution for cross-origin AJAX requests.

  1. Ordinary cross-domain request: only need to set Access-Control-Allow-Origin on the server side
  2. Cross-domain request with cookie: both front and back ends need to be set

[Front-end settings] Determine whether there is a cookie according to the xhr.withCredentials field

(1) Native AJAX

var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
 
// 前端设置是否带cookie
xhr.withCredentials = true;
 
xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
 
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);
    }
};

②jQuery ajax 

$.ajax({
   url: 'http://www.test.com:8080/login',
   type: 'get',
   data: {},
   xhrFields: {
       withCredentials: true    // 前端设置是否带cookie
   },
   crossDomain: true,   // 会让请求头中包含跨域的额外信息,但不会含cookie
});

③vue-resource

Vue.http.options.credentials = true

④ axios 

axios.defaults.withCredentials = true

[Server Settings]

The server supports CORS mainly by setting Access-Control-Allow-Origin. If the browser detects the corresponding settings, it can allow ajax to conduct cross-domain access.

① Java background

/*
 * 导入包:import javax.servlet.http.HttpServletResponse;
 * 接口参数中定义:HttpServletResponse response
 */
 
// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 
 
// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true"); 
 
// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");

② Nodejs background

var http = require('http');
var server = http.createServer();
var qs = require('querystring');
 
server.on('request', function(req, res) {
    var postData = '';
 
    // 数据块接收中
    req.addListener('data', function(chunk) {
        postData += chunk;
    });
 
    // 数据接收完毕
    req.addListener('end', function() {
        postData = qs.parse(postData);
 
        // 跨域后台设置
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie
            'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域(协议+域名+端口)
            /* 
             * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
             * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
             */
            'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是让js无法读取cookie
        });
 
        res.write(JSON.stringify(postData));
        res.end();
    });
});
 
server.listen('8080');
console.log('Server is running at port 8080...');

③ Apache needs to use the mod_headers module to activate the HTTP header settings, which is activated by default. You only need to add the following content to the configuration of <Directory>, <Location>, <Files> or <VirtualHost> in the Apache configuration file

Header set Access-Control-Allow-Origin *

 

Previous: Summary of Java basic knowledge (absolutely classic)

Next: [Summary of the most complete Java framework in the full stack] SSH, SSM, Springboot

Guess you like

Origin blog.csdn.net/guorui_java/article/details/109874891