同源策略
概念
浏览器的同源策略是一种安全机制,用于保护用户信息和防止恶意代码的执行。它是由浏览器实施的一组规则,限制了不同源(origin)的网页之间的交互。
同源是指两个网页具有相同的协议(protocol),主机(host)和端口号(port)。如果两个网页不满足同源条件,它们就属于不同的源,同源策略将应用。
同源策略的主要目标是防止恶意网页通过脚本等手段访问其他源的敏感数据,或者通过篡改其他源的网页内容来进行攻.击。以下是同源策略的一些关键限制:
DOM访问限制
不同源的网页不能直接访问彼此的DOM元素,包括读取和修改。这意味着一个网页无法通过JavaScript获取另一个网页的内容,除非目标网页明确授权。
Cookie限制
同源策略阻止网页访问不属于自己源的Cookie。Cookie是用于在客户端存储和传输信息的机制,同源策略确保Cookie只能由创建它的源访问。
XMLHttpRequest限制
XMLHttpRequest(XHR)是用于在网页和服务器之间进行异步数据交换的技术。同源策略禁止不同源的网页通过XHR请求发送或接收数据。
跨文档消息限制
同源策略限制不同源的窗口或帧之间通过postMessage()方法进行通信。这可以防止恶意网页滥用通信渠道。
脚本限制
不同源的脚本文件(如JavaScript)不能相互引用和执行。
尽管同源策略提供了一定的安全保护,但也可能限制了某些合法的跨域交互。为了支持安全的跨域通信,浏览器引入了一些机制,如跨域资源共享(CORS)和跨文档消息机制(postMessage),允许网页在一些受控的条件下进行跨域交互。
在发起跨域请求时,浏览器干了些啥
当浏览器发起跨域请求时,它会执行以下步骤:
发送请求头
浏览器向目标服务器发送一个请求,其中包含了请求方法(GET、POST等)和请求的URL。
检查同源策略
浏览器会检查目标URL是否符合同源策略。它会比较目标URL的协议、主机和端口号与当前网页的协议、主机和端口号是否一致。如果不一致,就会触发跨域请求。
发送跨域请求
如果目标URL与当前网页不同源,浏览器会发送一个跨域请求。跨域请求通常是一个HTTP OPTIONS 预检请求(preflight request),用于检查目标服务器是否允许跨域请求。
服务器处理预检请求
目标服务器接收到预检请求后,会进行一系列的处理。它会检查请求中的一些特定头部信息,如Origin
和Access-Control-Request-Method
,来验证是否允许跨域请求。
发送响应头
如果服务器允许跨域请求,它会在响应中添加一些特定的头部信息,如Access-Control-Allow-Origin
和Access-Control-Allow-Methods
。这些头部信息告诉浏览器该请求是被允许的。
检查响应头
浏览器接收到服务器的响应后,会检查响应中的头部信息。它会查看Access-Control-Allow-Origin
头部,判断是否允许当前网页进行跨域请求。
处理响应数据
如果服务器允许跨域请求,浏览器会将响应数据返回给发起请求的网页。否则,浏览器将拒绝访问响应数据,并在控制台中报错。
以上是跨域请求的基本流程。需要注意的是,跨域请求的处理还可能涉及其他一些情况,如带有凭据(credentials)的请求、特定的请求方法(如PUT、DELETE)等,这些都需要根据具体的场景进行处理。
解决跨域问题
在跨域问题中,解决的方法有以下几种:
跨域资源共享(CORS)
CORS 是一种在浏览器和服务器之间进行跨域通信的机制。通过在服务器端设置响应头信息,如 Access-Control-Allow-Origin
,可以允许特定的源(或所有源)访问服务器资源。服务器可以设置允许跨域访问的源、请求方法和请求头等。
利用nginx对所有预检请求返回可跨域响应头
可以在 Nginx 的配置文件中添加相应的配置。以下是一个示例配置:
location / {
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
# 其他请求的处理配置...
}
在上述配置中,我们使用 location /
来匹配所有请求。当请求方法为 OPTIONS 时,即预检请求,我们通过 add_header
指令添加了一系列的响应头,包括允许的源 Access-Control-Allow-Origin
、允许的方法 Access-Control-Allow-Methods
、允许的请求头 Access-Control-Allow-Headers
,以及预检结果的有效时间 Access-Control-Max-Age
。然后通过 return 204
返回一个空响应体和状态码 204,表示成功处理预检请求。
请注意,将以上配置添加到 Nginx 配置文件中后,需要重新加载或重启 Nginx 服务才能生效。
这样配置后,Nginx 将对所有预检请求都返回相应的可跨域响应头,从而允许跨域请求。但请注意,这样的配置是开放性的,将允许任何源进行跨域请求,因此在生产环境中,应根据具体需求和安全考虑进行细化配置,以限制允许的源和请求头。
JSONP(JSON with Padding)
JSONP 是一种利用 <script>
标签进行跨域请求的方法。通过在目标服务器上动态生成一个包含回调函数的 JavaScript 脚本,然后将脚本作为响应返回给客户端。客户端在接收到脚本后,会执行回调函数并处理返回的数据。
代理服务器
使用代理服务器是一种常见的跨域解决方案。在同源策略限制下,浏览器无法直接访问不同源的资源,但可以通过同源的服务器作为代理来请求目标资源。客户端向同源的服务器发起请求,服务器再将请求转发到目标服务器,并将目标服务器的响应返回给客户端。
假设,使用nginx作为代理的方式来解决跨域问题,我的web应用端口在8080端口,我的服务端地址在8081端口,服务端的接口均以/api前缀开头,下面是一个示例的配置
server {
listen 80;
server_name ;
location /api {
proxy_pass
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 可选配置,用于解决跨域请求的限制
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';
}
location / {
proxy_pass
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
iframe 通信
在同一个域名下,可以通过 iframe 元素实现不同页面之间的通信。通过在主页面嵌入一个隐藏的 iframe,将目标页面加载到 iframe 中,并通过在主页面和 iframe 页面之间使用 postMessage()
方法进行消息传递,从而实现跨域的数据交换。
WebSocket
WebSocket 是一种支持双向通信的网络协议,它在客户端和服务器之间建立持久的连接。WebSocket 不受同源策略的限制,因此可以在不同源的页面之间进行实时数据传输和通信。
这些方法中,CORS 是目前被广泛使用和推荐的跨域解决方案。它提供了更灵活和安全的机制来控制跨域访问,同时也是符合 Web 标准的方式。其他方法如 JSONP、代理服务器、iframe 通信和 WebSocket 则根据具体的需求和场景进行选择和使用。