跨源(跨域)
定义
翻译问题,实际是跨源 origin = protocol + domain + port
出于安全原因,浏览器限制从脚本内发起的跨源 HTTP 请求。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略。 这意味着使用 这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非使用 CORS 头文件。
- 服务端设置允许:Access-Control-Allow-Origin
- jsonp 伪跨域,需要服务端配合
- 利用 Apache 转发 如何解决 Ajax 跨域请求不到的问题? - pig pig 的回答 - 知乎 同源策略和跨域访问 - lg2045 的个人空间 - 开源中国社区
- gulp 代理插件 gulp-connect-proxy 浅谈 WEB 跨域的实现(前端向) - vajoy - 博客园 Javascript 跨域访问解决方案 - 老唐 的专栏 - 博客频道 - CSDN.NET ajax 跨域问题解决方案 | w3cboy,最专业的前端开发博客
浏览器拦截只是页面拿不到数据,请求是正常收发的
分类
请求跨域 Client-Server
- jsonp
- cors
页面跨域 Page-Page
- postMessage
- document.domain(不推荐,标准已移除)
JSONP
由于同源策略的限制,XmlHttpRequest
只允许请求当前源,script
标签没有同源限制
但是现在浏览器,默认会检查 MIME-type,如 script 请求 json 会被 CORB 拦截
import jsonp from 'jsonp-es6'
axios/COOKBOOK.md at master · mzabriskie/axiosjsonp跨域资源引起CORB_记忆阁楼 - SegmentFault 思否Fetch Standard CORB
jsonp 数据结构
jsonpcallback({
"id": 1,
"room": "main bedroom",
"items": [ "bed", "chest of drawers" ]
});
服务端代码,返回的是一个函数调用,数据作为参数
//用回调函数名称包裹返回数据
String result = callback + "(" + jsonData + ")";
response.getWriter().write(result);
客户端代码,请求文件 MIME type 应该是 javascript
function requestJSONP(url) {
// create script with passed in URL
var script = document.createElement('script');
script.src = url;
script.async = true;
// after the script is loaded (and executed), remove it
script.onload = function () {
this.remove();
};
// insert script tag into the DOM (append to <head>)
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
}
var url = "https://api.map.baidu.com/place/v2/search?query=ATM机&tag=银行®ion=北京&output=json&ak=F552bedbee2ec8fa6bae7b7a08201&callback=callback";
requestJSONP(url)
var callback = function (data) {
var json = JSON.stringify(data);
console.log(json);
};
CORS
服务器通过设置 Access-Control-Allow-Origin 来指定接受哪些域(以下仅用于跨域情况)
需要服务器设置支持
Access-Control-Allow-Origin 有两种情况,一个精确域名或
*
如何配置多个,写多条?缓存允许多个访问需要设置
vary
Access-Control-Allow-Credentials 表示是否允许发送 cookie,只能是 true,不需要就不要写Access-Control-Allow-Credentials 读取跨域响应内容限制,客、服均需设置
cookie 比较敏感,需要两端配合,才能传送,且只能同源,域名要求至少有两个点,localhost 不符合
不管是否为跨域请求,ORIGIN 字段总是被发送
Chrome/Firefox 不允许 https 向 http 发跨域请求,会被拦截
绕过浏览器 SOP,跨站窃取信息:CORS 配置安全漏洞报告及最佳部署实践 | Jianjun Chen's Homepage
javascript - Http requests withCredentials what is this and why using it? - Stack Overflow
axios 的 cookie 跨域以及相关配置 - 个人文章 - SegmentFault 思否
http - Set-Cookie header has no effect - Stack Overflow
webpack-dev-server 代理解决 cookie 丢失问题 - 掘金
postMessage
html5 api,页面与 service worker 通信用的就是这个
出于安全考虑,需要做 origin 判断
窗口是 open/iframe 关系
用法:
js// 父 $iframeEl.contentWindow.postMessage() // 子 window.parent.postMessage(res, "*"); window.addEventListener("message", (event) => {})
发送的数据对象会经过 structured clone algorithm ,递归 + 维护一份已访问对象引用 map,避免循环引用。
发散:页面通信还有哪些方法
storage event 作用于 localStorage/sessionStorage 共享的页面
jslet o = window.open('http://baidu.com') o.focus()
即使同源,并不能访问或修改 window 下的大部分变量
开发模式代理设置
create-react-app
可在 package.json 中设置proxy: "http://localhost:8080"
,要配合 fetch/ajax 使用vue-cli
创建的可在config.js/index.js
中设置- 要代理跨域请求,请求必须指向代理地址(localhost)才 work
dev: {
// ...
proxyTable: {
'/api': {
target: 'http://localhost:8081',
// changeOrigin: true // 不是必须?
pathRewrite: {
'^/api': '' // 重写接口
}
},
// http://example.com/result/xxxx.mp4
'/result': {
target: 'http://example.com',
}
},
devServer: {
...
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
}
}
Vue-cli proxyTable 解决开发环境的跨域问题 - 简书JS 中关于跨域及实现方法 | plainnany使用 vue-axios 和 vue-resource 解决 vue 中调用网易云接口跨域的问题 - 个人文章 - SegmentFault前端跨域问题及解决方案 · Issue #2 · wengjq/Blog由同源策略到前端跨域 | louis blog