✍前言
你好,我是方同学(YourBatman)
A哥 -> 方同学。是的,中文昵称改了。自知道行不深无以用“哥”字称呼,虽已毕业多年,同学二字寄寓心态一直积极、热情、年轻
挖掘机技术哪家强,山东技校找蓝翔;跨域问题怎么解,CORS还是JSONP?
关于浏览器跨域问题的解决方案,坊间一直“传闻”着两种解决方案:JSONP和CORS。由于文章的历史背景不同,作者偏好不一样,搞得好些同学迷惑得很,去谷歌里百度搜寻答案时经常就是这种赶脚。
作为一家负责任的“技校”(负责人的技术专栏),今天通过此文彻底给你解释清楚并给出确定的答案,助你快速选择正确的道路解决问题。
所属专栏
点拨-Cors跨域
本文提纲
版本约定
- JDK:8
- Servlet:4.x
- tomcat:9.x
✍正文
同源策略是浏览器最核心也最基本的安全功能。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。但是呢,在现在的互联网场景中,跨域访问是一种必须,所以才有了解决跨域问题的方案。
两大方案:JSONP和CORS
对于跨域共享资源,一共有两大解决方案
- JSONP:老一代浏览器解决方案
- CORS:全新一套标准的解决方案
JSONP方案
和iPhone 7和iPhone 7P不一样,JSONP 不等于 JSON Plus,全称是JSON with Padding。JSON是一种基于文本的数据交换格式,而JSONP是一种使用模式,可以让网页从别的域访问资源,从而完成跨域资源共享。
本系列第一篇文章就说到:<script>标签的src是没有跨域这么一说的,可以基于这一点实现Get请求的跨域。
JSONP的实现跨域的基本原理是:利用script标签的src没有跨域限制 + 回调的方式来完成跨域访问。
代码实现示例
前端页面:托管在63342端口
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSONP跨域请求</title> <!--导入Jquery--> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script> </head> <body> <script> // 请求完成后会回调此函数 function jsonpCallback(result) { console.log("这是JSONP请求的响应结果:" + result); } </script> <!--注:这个script必须放在上面function的下面--> <script type="text/javascript" src="http://localhost:8080/jsonp?callback=jsonpCallback"></script> </body> </html>
说明:利用script的src发送http请求到服务端,因此此script标签务必放在function的下面(因为浏览器是从上至下渲染)
服务端代码:托管在8080端口
/** * 在此处添加备注信息 * * @author YourBatman. <a href=mailto:yourbatman@aliyun.com>Send email to me</a> * @site https://yourbatman.cn * @date 2021/6/9 10:36 * @since 0.0.1 */ @Slf4j @WebServlet(urlPatterns = "/jsonp") public class JSONPServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String callback = req.getParameter("callback"); resp.getWriter().write(callback + "('hello jsonp...')"); } }
说明:可以看到服务端的代码非常的清爽,不涉及到任何请求头/响应头
打开页面,发送JSONP请求,结果如下:
请求的响应体:
浏览器控制台输出:
完美。通过JSONP我们实现了访问不同域的资源,实现了跨域。
用jQuery的ajax发送异步JSONP请求
上例是使用<script>标签的src属性发送同步跨域请求,在实际开发中(特别是前后端分离)大多数情况下发送的均为Ajax异步请求,下面来试试。
说明:异步请求用原生XMLHttpRequest还是Ajax或者Promis方式发出,底层原理都归一是相同的
使用jQuery发送异步JSONP请求非常的简单,连<script>和函数都不用写:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSONP跨域请求</title> <!--导入Jquery--> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script> </head> <body> <!--<script>--> <!-- // 请求完成后会回调此函数--> <!-- function jsonpCallback(result) {--> <!-- console.log("这是JSONP请求的响应结果:" + result);--> <!-- }--> <!--</script>--> <!--<!–注:这个script必须放在上面function的下面–>--> <!--<script type="text/javascript" src="http://localhost:8080/jsonp?callback=jsonpCallback"></script>--> <script> $.ajax({ // type: "get", // 不用写方法,因为JSONP只支持GET请求 url: "http://localhost:8080/jsonp", // 使用jQuery的Ajax后面是没有参数 dataType: 'jsonp', success: function (data) { console.log("这是JSONP请求的响应结果(jQuery Ajax):" + data); }, }); </script> </body> </html>
通过jQuery大大改善了js代码的书写方式,使得结构更加优雅、直观。这就是jQuery最厉害的语法糖能力~
说明:JsonP only works with type: GET。也就说type即使你写成post,jQuery也会给你转成get
服务端不变,发送异步请求,结果如下:
关注点:
- Ajax的callback回调函数名是动态生成的,并且确保了唯一性
- 由于服务端并不关心回调的函数名名称,因此回调函数名的长短没有关系(浏览器自己能识别就成)
影响体如下:
浏览器控制台打印:
完美。对于有技术敏感性的你来讲,应该能发现底层原理依旧还是script的src,只是写法不一样,仅此而已。
优缺点
JSONP跨域方案作为一种“古老”方式,有如下优缺点:
优点:
- 对老浏览器(如IE8、7等)有非常好的兼容性
- 书写起来比较简单,容易理解(毕竟没有那么多的请求头、响应头需要考虑嘛)
缺点:
- 只能发送get请求,不支持POST、PUT等请求方式,这是硬伤
- 安全度不高。因为JSONP是利用函数回调来由浏览器执行目标函数,这样宿主web其实是比较容易受到各类攻击的
总的来讲,随着Cors规范在2014年的正式确定,现代的浏览器100%支持Cors规范。由于浏览器的更新换代,JSONP的最大优势(兼容老浏览器)也就不复存在了,所以在实际开发中的使用建议是:不要使用JSONP,而应拥抱CORS。
CORS方案
由于JSONP方案存在一些不足(比如只支持Get请求就是硬伤),并不能很好的满足对跨域资源共享的需求,因此就出现了当下主流的跨域规范:CORS(Cross-origin resource sharing)
不同于JSONP的方案,CORS方案更强大实用,但稍微复杂那么一丢丢。其背后的基本思想是:使用自定义的HTTP头部和浏览器“沟通”,让浏览器和服务器相互“了解”对方,从而决定请求或响应成功与否。
说明:CORS 并不是为了解决服务端安全问题而出现,而是为了解决如何跨域调用资源。至于如何设计出安全的、开放的API,这就是安全范畴了(如可加上token验证、请求有效期、ip来源验证等手段)
CORS的WD(工作草案)从2009-03-17开始,2014-01-16进入REC(推荐标准)阶段,可谓正式毕业。起初CORS的推广的主要障碍是当时市面上的老浏览器并不支持它(比如当时市场占有率极大的IE 6、7、8这种老家伙),毕竟这个规范是新的只有升级的新浏览器才会支持到。
但历史的巨轮永远是滚滚向前,现在已经2021年了,现今市面上的浏览器对CORS规范的支持情况如下图所示(数据来源于:http://caniuse.com):
看到这张图,应该可以毫不客气的说:所有的浏览器(包括手机、PAD等浏览器)均已支持CORS规范
版本上拿Chrome浏览器举例:我现在使用的版本是91.0.xxxx.xxx
,完美支持:
当下阶段,已完全无需考虑浏览器兼容问题,所以JSONP的优势也就不复存在,可以放心的、积极的拥抱CORS。既然要用CORS,作为程序员不能只停留在概念上层面,接下来就来聊点干的,看看从实操层面有哪些具体做法落地CORS呢?
CORS的核心要义是和服务端和浏览器进行沟通,服务端架构一般是分层的,理论上可以在任意层次完成沟通。从负责完成沟通的层次上来讲,一般分为这两大类:
- 代理服务器/网关负责
- Web应用自行负责