Open ChuChencheng opened 4 years ago
为什么会有跨域?因为浏览器有个 同源策略
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
简单来说,同源策略限制了不同源之间资源的访问。假设你部署了一个应用 A ,别人在另一个域名上部署了应用 B ,那么,在同源策略的限制下,应用 B 就没法直接通过 ajax 请求应用 A 的东西。
如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。
例如,对于 http://www.aaa.com 来说:
http://www.aaa.com
http://www.aaa.com/bbb // 同源,只有路径不同 https://www.aaa.com // 不同源,协议不同 http://mail.aaa.com // 不同源,域名不同 http://www.aaa.com:8080 // 不同源,端口不同
同源策略会限制以下情况的跨域访问:
XMLHttpRequest
fetch
@font-face
drawImage
跨域资源共享(Cross-Origin Resource Sharing)是一种机制,通过使用额外的 HTTP 头来确认是否可以进行跨域访问
当请求满足一定的条件时,不会触发 CORS 预检请求 (即在请求之前再发送一个 OPTIONS 请求),这类请求称为 “简单请求”
OPTIONS
满足以下所有条件即可视为 “简单请求” :
GET
POST
HEAD
text/plain
multipart/form-data
application/x-www-form-urlencoded
预检请求要求首先使用 OPTIONS 方法发起一个预检请求道服务器,以获知服务器是否允许该实际请求,可避免跨域请求对服务器的用户数据产生未知的影响。
当请求满足一下任一条件时,应先发送预检请求:
PUT
DELETE
CONNECT
TRACE
PATCH
语法:
Access-Control-Allow-Origin: <origin> | *
可以指定为通配符 * 表示允许所有来源的跨域访问
*
或者指定一个特定的域名
跨域访问时, XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到一些最基本的响应头,如果需要拿到其他的响应头,需要在服务器端设置响应头的 Access-Control-Expose-Headers 字段,相当于一个白名单,如:
getResponseHeader()
Access-Control-Expose-Headers
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
这样, getResponseHeader() 即可访问到 X-My-Custom-Header, X-Another-Custom-Header 响应头了。
X-My-Custom-Header
X-Another-Custom-Header
这个响应头表示 预检请求 的结果能被缓存多久
Access-Control-Max-Age: <delta-seconds>
delta-seconds 表示缓存的秒数
delta-seconds
指定当浏览器的 credentials 设置为 true 时,在跨域请求时会带上 Cookies 进行身份验证。如果是简单请求,但没有带上 Access-Control-Allow-Credentials: true ,这时浏览器不会将相应的响应内容返回给请求的发起者,响应会被忽略;如果是 预检请求 ,带上 Access-Control-Allow-Credentials: true 则表示是否可以使用 credentials
credentials
Access-Control-Allow-Credentials: true
用于 预检请求 ,指明实际请求所允许使用的 HTTP 方法
Access-Control-Allow-Methods: <method>[, <method>]*
用于 预检请求 ,指明实际请求中允许携带的头部字段
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
表明 预检请求 或 实际请求 的源站
用于 预检请求 ,表示将实际请求所使用的 HTTP 方法告诉服务器
Access-Control-Request-Method: <method>
用于 预检请求 ,表示将实际请求所携带的头部字段告诉服务器
Access-Control-Request-Headers: <field-name>[, <field-name>]*
利用 script 标签没有同源限制的特点
script
优点: 兼容性好,可跨域 缺点: 只支持 get 方法,可能会受到 XSS 攻击
window.jsonpCallback = (responseData) => { // 处理跨域请求返回的数据 }
src
/cross-origin-api/getUser?callback=jsonpCallback
'jsonpCallback({ data: 666 })'
// 也就是执行了: window.jsonpCallback({ data: 666 })
postMessage 可以安全地实现跨域通信,一般用在以下场景:
语法如下:
otherWindow.postMessage(message, targetOrigin, [transfer])
在其他窗口中,只要在 window 下监听 message 事件,即可收到消息。
window
message
需要注意的是,收到消息时记得要判断一下消息是否来自期望的源,把非预期的源的消息都过滤掉,保证安全。
WebSocket 是一种双向的通信协议,可以跨域使用,在建立连接时使用的是 HTTP 协议,之后的通信就与 HTTP 无关了。
同源策略是对浏览器与服务端之间通信的限制,服务端之间并没有这个限制。
那么,只要在中间再搭一层 Nginx ,其服务与浏览器同源,与实际的业务服务端不同源,在浏览器对 Nginx 服务器发起请求时,将请求转发到实际的业务服务器,即可解决跨域问题。
为什么会有跨域问题
为什么会有跨域?因为浏览器有个 同源策略
简单来说,同源策略限制了不同源之间资源的访问。假设你部署了一个应用 A ,别人在另一个域名上部署了应用 B ,那么,在同源策略的限制下,应用 B 就没法直接通过 ajax 请求应用 A 的东西。
同源的定义
例如,对于
http://www.aaa.com
来说:同源策略的限制
同源策略会限制以下情况的跨域访问:
XMLHttpRequest
或fetch
发起的 HTTP 请求@font-face
使用跨域字体资源)drawImage
将 Images/video 画面绘制到 canvas如何允许跨域访问
CORS
跨域资源共享(Cross-Origin Resource Sharing)是一种机制,通过使用额外的 HTTP 头来确认是否可以进行跨域访问
简单请求
当请求满足一定的条件时,不会触发 CORS 预检请求 (即在请求之前再发送一个
OPTIONS
请求),这类请求称为 “简单请求”满足以下所有条件即可视为 “简单请求” :
GET
,POST
,HEAD
text/plain
,multipart/form-data
,application/x-www-form-urlencoded
三个值之一预检请求
预检请求要求首先使用
OPTIONS
方法发起一个预检请求道服务器,以获知服务器是否允许该实际请求,可避免跨域请求对服务器的用户数据产生未知的影响。当请求满足一下任一条件时,应先发送预检请求:
PUT
,DELETE
,CONNECT
,OPTIONS
,TRACE
,PATCH
中的任一方法HTTP 响应头字段
Access-Control-Allow-Origin
语法:
可以指定为通配符
*
表示允许所有来源的跨域访问或者指定一个特定的域名
Access-Control-Expose-Headers
跨域访问时,
XMLHttpRequest
对象的getResponseHeader()
方法只能拿到一些最基本的响应头,如果需要拿到其他的响应头,需要在服务器端设置响应头的Access-Control-Expose-Headers
字段,相当于一个白名单,如:这样,
getResponseHeader()
即可访问到X-My-Custom-Header
,X-Another-Custom-Header
响应头了。Access-Control-Max-Age
这个响应头表示 预检请求 的结果能被缓存多久
语法:
delta-seconds
表示缓存的秒数Access-Control-Allow-Credentials
指定当浏览器的
credentials
设置为 true 时,在跨域请求时会带上 Cookies 进行身份验证。如果是简单请求,但没有带上Access-Control-Allow-Credentials: true
,这时浏览器不会将相应的响应内容返回给请求的发起者,响应会被忽略;如果是 预检请求 ,带上Access-Control-Allow-Credentials: true
则表示是否可以使用credentials
Access-Control-Allow-Methods
用于 预检请求 ,指明实际请求所允许使用的 HTTP 方法
语法:
Access-Control-Allow-Headers
用于 预检请求 ,指明实际请求中允许携带的头部字段
语法:
HTTP 请求头字段
Origin
表明 预检请求 或 实际请求 的源站
Access-Control-Request-Method
用于 预检请求 ,表示将实际请求所使用的 HTTP 方法告诉服务器
语法:
Access-Control-Request-Headers
用于 预检请求 ,表示将实际请求所携带的头部字段告诉服务器
语法:
JSONP
原理
利用
script
标签没有同源限制的特点优缺点
优点: 兼容性好,可跨域 缺点: 只支持 get 方法,可能会受到 XSS 攻击
流程
script
标签,src
为跨域 API 的地址,将第一步定义好的回调函数名附在地址上,例如/cross-origin-api/getUser?callback=jsonpCallback
'jsonpCallback({ data: 666 })'
,将生成的字符串返回给浏览器postMessage
postMessage 可以安全地实现跨域通信,一般用在以下场景:
语法如下:
在其他窗口中,只要在
window
下监听message
事件,即可收到消息。需要注意的是,收到消息时记得要判断一下消息是否来自期望的源,把非预期的源的消息都过滤掉,保证安全。
WebSocket
WebSocket 是一种双向的通信协议,可以跨域使用,在建立连接时使用的是 HTTP 协议,之后的通信就与 HTTP 无关了。
Nginx 代理
同源策略是对浏览器与服务端之间通信的限制,服务端之间并没有这个限制。
那么,只要在中间再搭一层 Nginx ,其服务与浏览器同源,与实际的业务服务端不同源,在浏览器对 Nginx 服务器发起请求时,将请求转发到实际的业务服务器,即可解决跨域问题。
window.name + iframe
location.hash + iframe
document.domain + iframe
参考