前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >什么是跨域?一文弄懂跨域的全部解决方法

什么是跨域?一文弄懂跨域的全部解决方法

作者头像
程序员洲洲
发布2024-06-07 14:52:01
1910
发布2024-06-07 14:52:01
举报
文章被收录于专栏:项目文章项目文章

前言:为什么会有跨域?

跨域(Cross-Origin Resource Sharing,简称 CORS)是一种安全策略,用于限制一个域的网页如何与另一个域的资源进行交互。这是浏览器实现的同源策略(Same-Origin Policy)的一部分,旨在防止恶意网站通过一个域的网页访问另一个域的敏感数据。

由于浏览器实施的同源策略(Same Origin Policy),这是一种基本的安全协议,它确保了浏览器的稳定运行。没有同源策略,浏览器的许多功能可能无法正常工作。整个Web体系建立在同源策略之上,浏览器是这一策略的具体实现。该策略禁止来自不同域的JavaScript脚本与另一个域的资源进行交互。所谓同源,指的是两个页面必须具有相同的协议(protocol)、域名(host)和端口号(port)。

一、如何判断跨域?

很简单,只要当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。

比如下图这个例子,我们可以很好的分析。

二、非同源的限制

由于浏览器的同源策略限制,存在以下跨域问题:

  • 无法访问来自不同源网页的Cookie、LocalStorage和IndexedDB。这意味着不同源的网页之间不能共享存储数据。
  • 无法操作不同源网页的DOM。每个网页的DOM只能由其自己的脚本访问,不能被其他源的脚本操作。
  • 无法向不同源地址发起AJAX请求。这限制了网页与不同源服务器之间的数据交互。

这些限制确保了Web应用的安全性,防止恶意网站访问其他网站的敏感数据。但同时也给开发跨域Web应用带来了挑战,需要采取相应的跨域解决方案。

三、跨域解决方案

3.1 设置document.domain

我们可以通过设置document.domain解决无法读取非同源网页的 Cookie问题。

因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie(此方案仅限主域相同,子域不同的跨域应用场景。)

代码语言:javascript
复制
// 两个页面都设置以下代码即可
document.domain = 'test.com';

3.2 跨文档通信API

在Web开发中,跨文档消息传递是一个常见的需求,尤其是在使用iframe或弹出窗口时。postMessage方法提供了一种安全的方式来实现跨源通信,允许父窗口(parent window)和子窗口(child window)之间进行消息交换。以下是如何使用postMessage方法的一个示例:

1.父窗口发送消息到子窗口:

代码语言:javascript
复制
// 假设子窗口的URL是 http://test2.com
var childWindow = window.open('http://test2.com');
childWindow.postMessage('Hello, child window!', 'http://test2.com');

2.子窗口接收消息:

代码语言:javascript
复制
// 在子窗口中监听消息
window.addEventListener('message', function(event) {
    // 确保消息来源是可信的
    if (event.origin === 'http://test1.com') {
        console.log('Received message from parent window: ' + event.data);
    }
}, false);

3.子窗口发送消息回父窗口:

代码语言:javascript
复制
// 在子窗口中发送消息回父窗口
window.opener.postMessage('Hello, parent window!', 'http://test1.com');

4.父窗口接收来自子窗口的消息:

代码语言:javascript
复制
// 在父窗口中监听来自子窗口的消息
window.addEventListener('message', function(event) {
    if (event.origin === 'http://test2.com') {
        console.log('Received message from child window: ' + event.data);
    }
}, false);

使用postMessage方法可以解决以下方面的问题:

  • 跨域通信:允许不同源的窗口之间安全地交换信息,而不需要担心同源策略的限制。
  • 动态内容更新:父窗口可以向子窗口发送更新指令,子窗口根据这些指令更新页面内容。
  • 用户交互:子窗口可以响应用户操作,并将用户的交互结果发送回父窗口。
  • 安全性:通过检查event.origin属性,可以确保消息的来源是可信的,防止恶意网站发送伪造的消息。

调用postMessage方法实现父窗口http://test1.com向子窗口http://test2.com发消息(子窗口同样可以通过该方法发送消息给父窗口)

也就是它可用于解决以下方面的问题:

  1. 页面和其打开的新窗口的数据传递
  2. 多窗口之间消息传递
  3. 页面与嵌套的iframe消息传递
  4. 上面三个场景的跨域数据传递

3.3 JSONP

JSONP 是服务器与客户端跨源通信的常用方法。

最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。

核心思想:网页通过添加一个

原生方式实现代码:

代码语言:javascript
复制
<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实现代码:

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

Vue.js实现代码:

代码语言:javascript
复制
this.$http.jsonp('http://www.domain2.com:8080/login', {
    params: {},
    jsonp: 'handleCallback'
}).then((res) => {
    console.log(res); 
})

3.4 CORS

CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin 2、带cookie跨域请求:前后端都需要进行设置

前端只需要根据xhr.withCredentials字段判断是否带有cookie

1、原生Ajax实现方式

代码语言:javascript
复制
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);
    }
};

2、jQuery Ajax实现方式

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

3、Vue-resource

代码语言:javascript
复制
Vue.http.options.credentials = true

4、Axios

代码语言:javascript
复制
axios.defaults.withCredentials = true

服务端设置:

服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

Java实现方式

代码语言:javascript
复制
/*
 * 导入包: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后台

代码语言:javascript
复制
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...');

3.5 Webpack本地代理

在webpack.config.js中利用 WebpackDevServer 配置本地代理。

如下简单配置案例,这样 http://localhost:8080/api/getUser.php 的请求就是后端的接口 http://192.168.10.20:8088/getUser.php

代码语言:javascript
复制
    devServer: {
        port: 8080,
        proxy: {
            "/api": {
              target: "http://192.168.10.20:8088" // 后端接口
            }
        }
    }

3.6 Websocket

Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket 和 HTTP 都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 服务器与 客户端都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

3.7 Nginx反向代理

Nginx 作为一种高效的反向代理服务器,其工作原理与 Node.js 中间件代理相似,它允许开发者搭建一个中转服务器来转发请求。通过 Nginx 实现反向代理,可以轻松地解决跨域问题,这是一种简便且高效的解决方案。

具体来说,通过修改 Nginx 的配置文件,可以设置反向代理,将请求从一个服务器转发到另一个服务器。这种方式不仅适用于所有主流浏览器,而且支持 session 管理,无需对现有代码进行任何修改,也不会对服务器性能产生负面影响。

操作步骤如下:

1、在 Nginx 配置文件中,为需要代理的每个服务设置一个特定的前缀。 2、配置 Nginx 将这些前缀的 HTTP/HTTPS 请求转发到对应的真实服务器。 3、通过这种方式,所有通过 Nginx 转发的 URL 都将具有相同的域名、协议和端口号,从而满足浏览器的同源策略要求。

由于所有 URL 都指向同一个服务器,浏览器将它们视为同源,从而避免了跨域访问的限制。实际上,这些 URL 背后是由不同的物理服务器提供服务。这样,服务器内部的 JavaScript 代码就可以自由地跨域调用这些服务器上的资源。

先下载nginx,然后将 nginx 目录下的 nginx.conf 修改如下:

代码语言:javascript
复制
server {
 
    #nginx监听所有localhost:8080端口收到的请求
	listen       8080;
	server_name  localhost;
 
	# Load configuration files for the default server block.
	include /etc/nginx/default.d/*.conf;
    #localhost:8080 会被转发到这里
	#同时, 后端程序会接收到 "192.168.25.20:8088"这样的请求url
	location / {
		proxy_pass http://192.168.25.20:8088;
	}
	#localhost:8080/api/ 会被转发到这里
    #同时, 后端程序会接收到 "192.168.25.20:9000/api/"这样的请求url
	location /api/ {
		proxy_pass http://192.168.25.20:9000;
	}
	error_page 404 /404.html;
		location = /40x.html {
	}
	error_page 500 502 503 504 /50x.html;
		location = /50x.html {
	}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:为什么会有跨域?
  • 一、如何判断跨域?
  • 二、非同源的限制
  • 三、跨域解决方案
    • 3.1 设置document.domain
      • 3.2 跨文档通信API
        • 3.3 JSONP
          • 3.4 CORS
            • 3.5 Webpack本地代理
              • 3.6 Websocket
                • 3.7 Nginx反向代理
                相关产品与服务
                消息队列 TDMQ
                消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档