什么是同源政策?
定义
- 如果两个 URL 的协议、端口(如果有指定的话)和主机都相同的话,则这两个 URL 是同源的。
- 当一个网页加载的资源(如脚本、样式表、图片等)来自于与当前页面不同的域名、协议或端口时,就称为跨域请求。
举例:
http://store.company.com/dir/page.html
与http://store.company.com/dir2/other.html
同源http://store.company.com/dir/page.html
与http://news.company.com/dir/other.html
不同源,因为主机不同
参考:
为什么要有同源政策?
同源政策限制的是非同源的请求,那非同源请求会带来什么问题呢? 假设今天有一个使用者登入某一银行网站www.bank.com ,同时他刚好在使用另一个不安全的网站例如www.stolemoney.com ,如果没有同源政策的话,这个stolemoney 网站可能可以轻易地存取这个使用者在www.bank.com 里的资料。
浏览器的同源政策就像是最基本的一层保护机制,让不同源的网站无法存取到资源和资料。另外要注意的是,这个阻挡机制是在最后浏览器收到服务器端回应后发生的; 也就是说,就算是非同源请求,如果服务器端没有做任何阻挡、并回传结果,浏览器端其实是会成功收到回应,但因为违反同源政策,浏览器会拦截这个回应、并报错。
跨域问题的解决
三类标签
- 有类个标签允许跨域加载资源
<img src=XXX>
<link href=XXX>
<script src=XXX>
代理
- 原理:
- 同源策略只受限于浏览器访问服务器,对于服务器访问服务器并没有限制的特点。中间服务器做了一个请求转发的功能
- 实现:
- Node中间件代理
- nginx反向代理
- vite配置代理
CORS
- CORS 跨源资源共享(Cross-Origin Resource Sharing)。
- 总体思路是:如果浏览器要跨域访问服务器的资源,需要获得服务器的允许
- 三种请求
- 简单请求
- 需要同时满足以下两大条件
- 使用下列方法之一:GET HEAD POST
- Content-Type 的值仅限于下列三者之一:text/plain,multipart/form-data,application/x-www-form-urlencoded
- 需要同时满足以下两大条件
- 需要预检的请求
- 附带身份凭证的请求
- 简单请求
- 服务器通过响应header
Access-Control-Allow-Origin
进行控制 - 跨源资源共享 - MDN Web 文档术语表:Web 相关术语的定义 | MDN
postMessage
- 语法:
otherWindow.postMessage(message, targetOrigin, [transfer]);
- 过程
- 获得对另一个窗口的引用(比如 targetWindow = window.opener)
- 在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息
- 接收消息的窗口可以根据需要自由处理此事件。
- 传递给 window.postMessage() 的参数(比如 message)将通过消息事件对象暴露给接收消息的窗口。
- 安全问题
- 如果你不希望从其他网站接收 message,请不要为 message 事件添加任何事件侦听器。
- 如果你确实希望从其他网站接收 message,请始终使用 origin 和 source 属性验证发件人的身份。当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。
- 当你使用 postMessage 将数据发送到其他窗口时,始终指定精确的目标 origin,而不是 *。
- 参考: window.postMessage - Web API | MDN
websocket
wip
JSONP
- 服务端
app.get('/app/jsonp', (req, res) => {
const responseData = { data: [{ id: 1, name: 'Brian' }, { id: 2, name: 'Peter' }] }
const responseDataText = JSON.stringify(responseData)
res.set('Content-Type', 'application/text')
res.send(`jsonpCallback(${responseDataText})`)
})
- 网页端
...
<script>
function jsonpCallback(response) {
console.log('jsonp response', response)
// 任何你想對 data 執行的動作
}
</script>
<script type="text/javascript" src="/app/jsonp"></script>
</body>
实际遇到过的问题
- 描述:
一个页面里用到了三次同一个s3生成的signed url,一次是thumbnail,另一次是preview,第三次是在image的onload后使用fetch直接去获取s3的图片,我发现这里会使用缓存,显示是(memory cache)但这样的话它似乎就丢失了cors信息,就会报错
blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
- 代码如下
- 解决方法: 将出现的两个
<img>
的crossOrigin都设为"anonymous" - 有效的原因
- 跨域图像加载的缓存行为:通过设置
crossOrigin="anonymous"
,浏览器明确地按照跨域请求来处理这个图像,确保它从服务器获取时遵循 CORS 规范,并且缓存下来的资源携带正确的 CORS 信息。 - 避免重复跨域请求错误:由于图像是通过
<img>
标签加载且设置了 crossOrigin,接下来的 fetch 请求可以从缓存中获取具有正确 CORS 头的信息,避免了 CORS 错误。
- 跨域图像加载的缓存行为:通过设置
- 参考