一,什么是跨域
域(Origin)是由协议、域名和端口组成的,只有这三者完全一致的情况下,浏览器才会认为两个网址同源,否则就认为存在跨域。跨域是指在Web开发中,一个网页的JavaScript代码试图访问另一个域(网站)下的资源,如发送Ajax请求、读取Cookie、操作DOM等。
当一个请求url的协议,域名,端口三者之间任意一个与当前的url不同都即为跨域
从http://localhost:8601 到 http://localhost:8602 由于端口不同,是跨域。
从http://192.168.101.10:8601 到 http://192.168.101.11:8601 由于主机不同,是跨域。
从http://192.168.101.10:8601 到 https://192.168.101.10:8601 由于协议不同,是跨域。
注意:服务器之间不存在跨域请求。
跨域访问存在安全风险,因为不同的域名可能代表不同的网站,一个域名下的恶意代码可能试图访问另一个域名下的敏感数据,从而导致信息泄露和安全问题。
为了限制跨域访问,浏览器实现了同源策略(Same-Origin Policy),该策略默认情况下阻止了不同域之间的大部分交互。
二,非同源限制
-
DOM访问限制: 一个网页的JavaScript代码在访问另一个域下的DOM元素时受到限制。这意味着,如果一个网页试图通过JavaScript修改或操作不同域下的DOM元素,浏览器会阻止这种行为。
-
Ajax请求限制: XMLHttpRequest 或 Fetch API 发起的Ajax请求默认受到同源限制,不能直接访问不同域下的资源。这是因为浏览器会检查请求的目标域是否与当前页面的域相同,如果不同,请求会被拒绝。
-
Cookie限制: 不同域之间的 Cookie 信息通常是隔离的,一个域下的网页无法直接访问另一个域的 Cookie。
-
跨域脚本限制: 通过
<script>
标签引入的外部脚本通常可以跨域加载,但是它们在执行时的访问权限受到限制,例如无法访问其他域的全局变量和函数。
为了实现跨域访问,开发人员可以采用一些方法来绕过这些限制,例如使用服务器端代理、JSONP、CORS(Cross-Origin Resource Sharing)头部等。这些方法能够在服务器和客户端之间建立一些协议,以允许特定类型的跨域请求。
三,问题引入
我的前端放在8602端口,我的后端放在63110端口,然后我的前端需要去请求后端的数据,我用chrom浏览器,我按f12调试的时候发现报了这个错:
但是在edge浏览器中却没有报错,我就有点纳闷,然后搜了一下发现:
不同浏览器之间对跨域访问的限制程度可能会有所不同,这主要是因为每个浏览器厂商在实现同源策略(Same-Origin Policy)时可能会有一些差异。
就是说我这个属于因为端口不同跨域了,edge放我过去了chrom严一点就没放。
四,解决方案
1,JSONP
通过script标签的src属性进行跨域请求,如果服务端要响应内容则首先读取请求参数callback的值,callback是一个回调函数的名称,服务端读取callback的值后将响应内容通过调用callback函数的方式告诉请求方。如下图:
它不是通过Ajax请求,他是通过JavaScript标签请求,可以请求跨域地址,但是如何拿到数据呢?
JSONP的原理是利用<script>
标签的跨域加载特性,通过在页面中动态创建<script>
标签来加载一个外部脚本文件,而这个外部脚本文件是由服务器动态生成的,包含了要获取的数据,这个数据会被当作参数传递给一个预定义的回调函数,从而实现数据的跨域传输。
以下是JSONP的基本工作原理:
-
客户端(浏览器)发起请求: 客户端页面中的JavaScript代码通过创建一个
<script>
标签,将请求发送到其他域(服务器)。 -
服务器处理请求: 服务器接收到这个请求后,会根据请求参数动态生成一个包含要传递数据的JavaScript脚本。
-
服务器响应: 服务器将生成的JavaScript脚本作为响应返回给客户端。这个响应是一个可执行的脚本。
-
客户端解析响应: 浏览器解析服务器响应时,会立即执行脚本内容。这样,服务器返回的数据会被传递给预先定义好的回调函数。
由于<script>
标签的加载不受同源策略的限制,所以可以在不同域之间加载脚本文件。JSONP的关键是要确保服务器返回的脚本内容调用了一个事先定义好的回调函数,并将数据作为参数传递给这个回调函数。客户端需要在页面中提前定义这个回调函数,以便在脚本加载并执行后,能够正确处理从服务器返回的数据。
说的直白一点就是,服务端会返回一个js函数给前端,这个js函数前端事先已经定义好了,所以前端拿到之后就会立即执行,所以前后端是需要统一这个js回调函数的名字的,服务端把需要的数据写在了这个js函数的参数里面,所以前端拿到这个js的参数就拿到了数据
JSONP的优点是简单易用,但也存在一些安全风险,因为它需要在客户端预先定义一个全局回调函数,可能会被恶意网站滥用。另外,JSONP只支持GET请求,无法发送其他类型的HTTP请求。
2,添加响应头
浏览器判断是跨域请求会在请求头上添加origin,表示这个请求来源哪里。
比如:
GET / HTTP/1.1
Origin: http://localhost:8601
服务器收到请求判断这个Origin是否允许跨域,如果允许则在响应头中说明允许该来源的跨域请求,如下:
Access-Control-Allow-Origin:http://localhost:8601
如果允许任何域名来源的跨域请求,则响应如下:
Access-Control-Allow-Origin:*
常见的响应头字段: 以下是一些常见的响应头字段以及它们的作用:
-
Access-Control-Allow-Origin: 在CORS中使用,指定允许访问资源的域。例如,
Access-Control-Allow-Origin: *
表示允许任意域访问资源。 -
Content-Type: 指定响应体的MIME类型,告诉浏览器如何解析和显示响应内容。
-
Cache-Control: 控制缓存策略,例如
Cache-Control: no-cache
表示禁止缓存。 -
Location: 在重定向响应中,指定新的URL地址。
-
Set-Cookie: 在响应中设置Cookie。
3,通过nginx代理跨域
由于服务端之间没有跨域,浏览器通过nginx去访问跨域地址。
1)浏览器先访问http://192.168.101.10:8601 nginx提供的地址,进入页面
2)此页面要跨域访问http://192.168.101.11:8601 ,不能直接跨域访问http://www.baidu.com:8601 ,而是访问nginx的一个同源地址,比如:http://192.168.101.11:8601/api ,通过http://192.168.101.11:8601/api 的代理去访问http://www.baidu.com:8601。
这样就实现了跨域访问。
浏览器到http://192.168.101.11:8601/api 没有跨域
nginx到http://www.baidu.com:8601通过服务端通信,没有跨域。
客户端浏览器发起跨域请求时,会先将请求发送到Nginx代理服务器,Nginx会将请求转发到目标域,然后将目标域的响应返回给客户端。客户端浏览器认为请求是从同源发起的,因为请求的域与客户端页面的域一致,从而避免了跨域问题。