跨域的根本原因是浏览器的“同源策略”,得先了解什么是同源?—— 就是【协议+域名+端口号】相同,即为同源,只能向同源的服务发起AJAX请求。
源1 | 源2 | 是否同源 |
---|---|---|
a.com | b.com | 🚫不同源,域名不同 |
http://a.com | https://a.com | 🚫不同源,协议不同 |
a.com:80 | a.com:443 | 🚫不同源,端口不同 |
gg.com | a.gg.com | 🚫不同源,子域名不同 |
a.com/ss | a.com/s2 | 同源 |
可通过
location.origin
、window.origin
获取当前文档的源
❓为什么要同源呢?
这是浏览器故意设计的,是浏览器的基本安全策略,否则会很容易受到XSS、CSRF攻击。只能向同源的服务发起AJAX请求,不可跨域请求,会被浏览器拦截。
❓有哪些限制规则呢?
<img src="url">
、<link href="url">
、<script src="url">
元素获取的其他源的资源。随着互联网越来越复杂,需求也越来越多,跨域请求就很常见了。我们知道了跨域是浏览器的同源限制,就可以针对性的想办法了。
这是一种传统的跨域请求办法,借助于<script>
标签元素,因为<script>
的src
可以访问任何站点的资源。当然这需要服务端对应接口支持JSONP(JSON with padding)协议,所以是需要双方约定好,所以浏览器认为这是安全的。
<script>
标签,浏览器自动发起的资源请求。JSONP(JSON with Padding)是JSON的一种”使用模式“,是一种非官方的协议,用于解决浏览器的跨域数据访问的问题。
📢前端具体实现过程:
<script>
标签,src
为要跨域的API地址,URL中带上回调参数“callback=getData”。getData('data')
”。<script src="http:www.thrid.com/cors/api?q=key&callback=back"></script> <script> function back(data) { console.log(data); } </script>
JSONP的实现:
function jsonp(url, args, cbName) { return new Promise((resolve, reject) => { const ele = document.createElement('script'); window[cbName] = (data) => { resolve(data); document.body.removeChild(ele); } args = { ...args, callback: cbName }; ele.src = `${url}?${Object.keys(args).map(k => `${k}=${args[k]}`).join('&')}`; document.body.appendChild(ele); }); } //使用,api为360的公开接口 jsonp('https://sug.so.360.cn/suggest', { format: 'jsonp', word: 'china' }, 'search') .then(function (data) { console.log(data) });
CORS是什么?—— 跨域资源共享 (cross-origin resource sharing),让AJAX可以跨域访问数据。这是为了满足跨域请求的需求,W3C新增加的特性,需要服务端的支持,不支持IE8/9。根据请求方式,浏览器将CORS分为两种情况:
基本原理就是在请求头加入一个身份来源标识,服务端根据这个标识来判等是否允许访问,如果允许则给一个允许的标记并返回响应。
📢具体过程比较简单,前端只要在Header加入“Origin”即可:
origin:http://www.main.com
GET /api HTTP/1.1 Origin: http://www.main.com //本次请求来自哪个源 Host: http://www.third.com //请求的第三方API Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0 ...
Access-Control-Allow-Origin: http://www.main.com //请求允许的源 Access-Control-Allow-Credentials: true //是否允许cookie,cors默认不发送cookie,如果要发送,还需AJAX中设置withCredentials Access-Control-Expose-Headers: Content-Length,API-Key //如果客户端想要访问其他非安全字段,则需要服务端明确定义哪些Header字段暴露出来 Content-Type: text/html; charset=utf-8
不是简单请求的都称为复杂请求(非简单请求),如请求方法是PUT、DELETE,或Content-Type=application/json
。相比于简单请求,复杂请求多了一次预请求。
预请求:
OPTIONS
方法。因为同源是浏览器的限制,跨域的方法无非就是绕过,或采用CORS。
跨域方案 | 基本原理 | 是否需要服务端支持 |
---|---|---|
JSONP | 借助<script> 标签的src ,加上一个全局回调函数接收数据 |
🟠需要服务端支持JSONP协议 |
CORS | W3C标准支持的跨域方式,请求头添加Origin 字段 |
🟠需要服务端支持 |
WebSocket | WebSocket可以实现浏览器与服务端的双向通信,没有跨域的困惑。推荐第三方库 Socket.io,可以很方便的建立与服务端的Socket通信。 | 🟠需要服务端支持,支持WebSocket |
iframe+postMessage | 使用window.postMessage() 来实现窗口之间的通信 |
🔵不需服务端处理,客户端绕过 |
服务端代理 | 由自己的同源服务端代理第三方的请求 | 🟠需要服务端支持,代理请求 |
nginx反向代理 | 原理和服务端代理一样,用nginx配置一个代理服务 | 🔵不需要服务端修改代码,需nginx支持 |
©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀