跨域

117 阅读4分钟

简介

跨域是指一个域下的文档或者脚本试图请求另一个域下的资源。这里的跨域是广义的

广义的跨域

1、资源跳转:链接,重定向,表单提交

2、资源嵌入:link script img frame等dom标签,样式中background:url(),@font-face()等文件外链

3、脚本请求:js发起的Ajax请求,dom和js对象的跨域的操作

当一个请求URL的协议,域名,端口三者之间任意一个与当前页面URL不同,即为跨域

跨域并不是请求发不出,请求可以正常发出去,服务端能收到请求并返回正常返回结果,只是结果被浏览器拦截了

需要特别注意两点:

第一:如果是协议和端口造成的跨域问题,前端是无能为力的

第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为协议,域名,端口必须匹配

为什么出现跨域问题

前端部分其实我们通常所说的跨域是狭义的,是由于浏览器同源策略限制的一类请求场景

出于浏览器的同源策略限制。同源策略/SOP(Sameoriginpolicy)是一种约定,他是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,浏览器很容易收到XSS,CSFR等攻击,否则浏览器的正常功能可能都会收到影响。可以说Web是构建在同源策略基础上的,浏览器只是针对于同源策略的一种实现。同源策略会组织一个域的JS脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

同源策略限制以下几种行为:

1、Cookie,LocalStorage和IndexDb无法获取

2、Dom和JS对象无法获得

3、Ajax请求不能发送

但是有三个标签是允许跨域加载资源的:

1. <img src = xxx>
2. <link href = xxx>
3. <script src = xxx>
    </script>

常见的跨域的场景

当前页面URL被请求页面URL是否跨域原因
www.test.com/www.test.com/index.html同源(协议、域名、端口号相同)
www.test.com/www.test.com/index.html跨域协议不同(http/https)
www.test.com/www.baidu.com/跨域主域名不同(test/baidu)
www.test.com/blog.test.com/跨域子域名不同(www/blog)
www.test.com:8080/www.test.com:7001/跨域端口号不同(8080/7001)
www.domain.com/a.jshttp://192.168.4.12/b.js跨域域名和域名对应相同IP,也非同源

跨域解决方法

但所有的跨域都必须经过信息提供方的允许。如果未经允许即可获取,那是浏览器同源策略出现漏洞。

Cors方式

后端代码

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") 
            .allowedOrigins("*") //允许访问来源
            .allowCredentials(true)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .maxAge(3600);
    }
}

前端代码

function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {
 
    // 此时即支持CORS的情况
    // 检查XMLHttpRequest对象是否有“withCredentials”属性
    // “withCredentials”仅存在于XMLHTTPRequest2对象里
    xhr.open(method, url, true);
 
  } else if (typeof!= "undefined") {
 
    // 否则检查是否支持XDomainRequest,IE8和IE9支持
    // XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式
    xhr = new XDomainRequest();
    xhr.open(method, url);
 
  } else {
 
    // 否则,浏览器不支持CORS
    xhr = null;
 
  }
  return xhr;
}
 
var xhr = createCORSRequest('GET', url);
if (!xhr) {
  throw new Error('CORS not supported');
}

Nginx

使用Nginx搭建企业级接口网关

www.a.a.com 不能直接请求 www.b.b.com 的内容,可以通过nginx,根据同域名,但项目名不同进行区分。

举个🌰:

假设公司域名为www.nginxtest.com

当我们需要访问 www.a.a.com 通过 www.nginxtest.com/A 访问,并通过nginx转发到 www.a.a.com

当我们需要访问 www.b.b.com 通过 www.nginxtest.com/B 访问,并通过 www.b.b.com

我们访问公司的域名时,是同源的,只是项目名不同,此时项目名的作用只是为了区分,方便转发。

配置如下:

server {
        listen       80;
        server_name  www.nginxtest.com;
        location /A {
		    proxy_pass  http://a.a.com:81;
			index  index.html index.htm;
        }
		location /B {
		    proxy_pass  http://b.b.com:81;
			index  index.html index.htm;
        }
    }

我们访问以 www.nginxtest.com 开头且端口为80的网址,nginx将会进行拦截匹配,若项目名为A,则分发到a.a.com:81.实际上就是通过同源的域名,不同的项目名进行区分,通过nginx拦截匹配,转发到对应的网址。整个过程,两次请求发,第一次请求nginx服务器,第二次服务器通过拦截匹配分发到对应的网址。