什么是同源策略
同源策略(Same-origin policy)是一个浏览器安全机制,用于限制不同源之间的跨域操作。它是一种控制浏览器如何处理来自不同源的资源(例如文档、脚本、样式表和AJAX请求)的规则。
在同源策略中,如果两个URL具有相同的协议(protocol),主机(host)和端口号(port),则它们被视为同源(origin)。
举个例子:
当一个浏览器的两个tab页中分别打开来自百度和谷歌的页面,浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
注意:跨域并不是请求发不出去,而是请求能发出去且服务端能收到请求并正常返回结果,只是结果被浏览器拦截。
假设某标准URL为http://store.company.com
跨域举例如下:
假设某标准URL为http://www.a.com
跨域举例如下:
提出目的及限制
同源策略的目的是保护用户的安全和隐私。它防止恶意网站通过跨域请求访问用户的敏感信息,或者利用其他网站上的漏洞进行攻击。
同源策略施加了以下限制:
- JavaScript 只能访问与其来源相同的页面和代码。
- DOM(文档对象模型)限制了对不同源文档的访问。
- XMLHttpRequest 和 Fetch API 限制了对不同源的 AJAX 请求。
了解同源策略对于编写安全的网络应用程序和了解跨域问题的解决方案至关重要。
然而,同源策略并不适用于所有场景。为了允许跨域请求,可以使用一些机制,如跨域资源共享(CORS)和服务器端代理等。接下来进行详细介绍。
JSONP跨域通信
JSONP(JSON with Padding)是一种利用<script>
标签的跨域通信技术,在跨域请求中使用动态创建<script>
标签来加载数据。以下是JSONP工作原理的步骤:
- 客户端创建一个动态
<script>
标签,并指定要请求的URL地址,同时定义一个回调函数,该回调函数在数据加载完成后被调用。 - 服务器接收到请求后,将数据包装在回调函数中的响应返回给客户端。
- 客户端的浏览器通过执行回调函数处理响应数据。
JSONP的关键点是服务器返回的数据需要包裹在预定义的回调函数中。这样,即使是跨域请求,客户端仍然可以通过动态创建<script>
标签加载数据,并在响应返回后执行预定义的回调函数。
举个例子:
假设我们有两个网站:https://www.example.com
和 https://api.example.com
。根据同源策略,这两个网站被认为是不同源的。
在 https://www.example.com
的页面中,我们想要获取来自 https://api.example.com/data
的数据,而且 https://api.example.com
并没有启用CORS,此时可以进行JSONP跨域通信。
-
在客户端,我们需要定义一个回调函数来处理获取到的数据:
function processData(data) { console.log(data); }
-
创建一个动态的
<script>
标签,将https://api.example.com/data
作为其src
属性值,并指定回调函数名作为查询参数,例如:var script = document.createElement('script'); script.src = 'https://api.example.com/data?callback=processData'; document.head.appendChild(script);
-
当浏览器加载该
<script>
标签时,会发送请求到https://api.example.com/data?callback=processData
,服务器应返回的数据包装在processData()
函数中,响应内容如下:processData({ "message": "This is cross-origin data" });
-
浏览器解析响应并执行
processData()
函数,客户端就可以访问到跨域请求的数据。
通过JSONP,我们可以在不启用CORS的情况下实现跨域请求。
需要注意的是: JSONP具有一定的局限性(如只支持GET请求、安全问题等),且它依赖于对外公开的回调函数,可能受到恶意代码注入的风险。但在不需要复杂跨域请求时,它是一种简单有效的解决方案。
跨域资源共享(CORS)
CORS 是一种机制,允许在浏览器中通过特定的 HTTP 头来控制跨域请求。它基于服务器的信任模型,服务器可以发送额外的响应头来指示浏览器是否允许跨域请求。当浏览器发起跨域请求时,会进行预检请求(OPTIONS 请求)来检查服务器允许的跨域操作。如果服务器返回了合适的 CORS 响应头,浏览器就会执行实际的跨域请求。
览器将CORS请求分成两类:简单请求和非简单请求。
简单请求需要满足以下两大条件:
-
请求方法是以下三种方法之一:HEAD、GET、POST。
-
HTTP的头信息不超出以下几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type只限于三个值
application/x-www-form-urlencoded、multipart/form-data、text/plain
CORS简单请求跨域实现流程:
同时,也可以在服务器端设置允许来自特定源的跨域请求:
# Python Flask 示例代码
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/data', methods=['GET'])
def get_data():
response = jsonify({'message': 'This is a cross-origin response'})
response.headers.add('Access-Control-Allow-Origin', 'https://www.example.com')
response.headers.add('Access-Control-Allow-Methods', 'GET')
return response
if __name__ == '__main__':
app.run()
上述代码中,Access-Control-Allow-Origin
和 Access-Control-Allow-Methods
是 CORS 相关的响应头,它们指示浏览器允许来自特定源的 GET 请求。
官方文档:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
服务器端代理
服务器端代理是一种通过在服务器上转发请求来处理跨域请求的方法。它涉及设置一个位于客户端和目标服务器之间的代理服务器,客户端将请求发送到代理服务器,代理服务器再将请求转发给目标服务器,并将响应返回给客户端。由于代理服务器与目标服务器在同源中,所以不存在跨域问题。
服务器代理实现流程:
例如,当客户端需要与 https://api.example.com/data
进行跨域通信时,可以通过服务器端设置一个代理路由 /proxy
,然后客户端将请求发送到 /proxy/data
,代理服务器再将该请求转发给 https://api.example.com/data
,并将响应返回给客户端。
这样,对于客户端来说,它发送的请求是在同源(相对于代理服务器)中进行的,避免了跨域问题。
反向代理
反向代理是一种代理服务器的布置方式,它将客户端请求转发到内部资源服务器,并将响应返回给客户端。与正向代理不同,反向代理透明地对外提供服务,客户端无需知道实际提供服务的服务器。
以下是反向代理的工作原理和一些关键概念:
-
客户端发送请求到反向代理服务器,如
https://www.example.com
。 -
反向代理服务器收到客户端请求后,根据预定义的规则和路由配置,决定将请求转发到哪个内部资源服务器进行处理。
-
反向代理服务器将请求转发到内部资源服务器,如
https://appserver1.example.com
。 -
内部资源服务器接收到请求后,根据其所提供的服务或内容,生成响应并发送回反向代理服务器。
-
反向代理服务器接收到来自内部资源服务器的响应后,将响应返回给客户端。
在实际应用中,反向代理可以使用常见的Web服务器软件(如Nginx、Apache)来配置和搭建。通过适当的配置,反向代理可以提供高性能、安全和可扩展的服务。
举个例子:
假设你有一个网站 https://www.example.com
,并希望在该网站上提供多个服务,如静态文件、API和应用程序。为了提高性能和安全性,你决定使用反向代理来管理这些服务。
(1)配置反向代理服务器:
你使用Nginx作为反向代理服务器,并进行相应的配置。
(2)静态文件服务:
在你的内部资源服务器上,你有一个用于存储静态文件的目录,如 /var/www/html
。你在Nginx的配置中指定将以 /static
路径开头的请求转发到该目录。例如:
location /static {
alias /var/www/html;
}
当客户端访问 https://www.example.com/static/file.jpg
时,Nginx会将请求转发到内部资源服务器的 /var/www/html/file.jpg
文件,并将响应返回给客户端。
(3)API服务:
你的内部资源服务器还提供了一组API接口,如 /api/getData
和 /api/saveData
。你在Nginx的配置中指定将以 /api
路径开头的请求转发到该API服务器。例如:
location /api {
proxy_pass http://api-server;
}
当客户端发起对 https://www.example.com/api/getData
的请求时,Nginx会将请求转发到内部资源服务器上的 API 服务器,并将响应返回给客户端。
通过配置反向代理服务器,你可以将不同路径的请求转发到内部的不同服务上,实现了服务的拆分和管理。客户端只需与反向代理服务器通信,无需直接访问内部资源服务器。
反向代理的多个优势和额外功能:
-
负载均衡:反向代理可以根据事先定义的算法将请求分发到多个内部资源服务器,实现负载均衡,提高性能和可靠性。
-
缓存和加速:反向代理可以缓存响应,减轻内部资源服务器的负担,并通过提供静态内容的本地副本加速响应时间。
-
安全过滤:反向代理可以在客户端和内部资源服务器之间进行安全过滤,保护内部资源服务器免受恶意攻击,如DDoS(分布式拒绝服务)攻击、SQL注入等。
-
隐藏内部结构:反向代理充当了外部访问的唯一入口,它可以隐藏内部资源服务器的真实IP地址和内部网络结构,增强了安全性。
总结
以上为同源策略(Same origin policy)及跨域请求相关知识点,具体介绍了JSONP跨域通信、CORS、服务器端代理、反向代理等相关技术,读者可深入了解。
我是秋说,我们下次见。