简介
跨域是指一个域下的文档或者脚本试图请求另一个域下的资源。这里的跨域是广义的
广义的跨域
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.js | http://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服务器,第二次服务器通过拦截匹配分发到对应的网址。