Golang Gin 实战(九)| JSONP跨域和劫持
浏览器都遵循同源策略,也就是说位于www.flysnow.org
下的网页是无法访问非www.flysnow.org
下的数据的,比如我们常见的AJAX跨域问题。
要解决跨域问题的办法有CORS、代理和JSONP,这里结合Gin,主要介绍JSONP模式
JSONP原理
JSONP可以跨域,主要是利用了<script>
跨域的能力,因为这个标签我们可以引用任何域名下的JS文件。既然是这样,我们就可以利用这个能力,在服务端生成相应的JS代码,并且把返回的Content-type
设置为application/javascript
即可。
在生成这个这个对应的JS代码的时候,就比较有讲究了,一般是调用客户端网页已经存在的JS函数。
|
|
以上我们定义了一个JS函数sayHello
,可以通过alert
的方式,显示对应的data
数据。
假设我们通过http://localhost:8080/jsonp?callback=sayHello
来调用sayHello
函数,那么我们这个URL输出的内容要是这样:
sayHello({"wechat":"flysnow_org"});
并且对应的Content-type
设置为application/javascript
,这样才能达到调用sayHello
函数的目的,也就是通过JSONP的方式解决了数据跨域读取的问题。
最终,我们的网页的源代码是这样的:
|
|
注意前后顺序,必须要先定义sayHello
函数。
Gin JSONP 实现
要通过Gin来实现服务端对JSONP的支持非常简单,只需要使用JSONP
函数即可。
|
|
它的使用方法和c.JSON
一模一样,第一个参数是HTTP Status Code,第二个是返回的数据。
|
|
通过上面的源代码,我们发现Gin指定callback
参数名作为接收回调函数的参数名,所以我们上面的例子,是通过http://localhost:8080/jsonp?callback=sayHello"
把sayHello
这个回调JS函数传递给服务端,这样服务端才会返回对应的JS类型的字符串,才能被浏览器执行,达到跨域的目的。
sayHello({"wechat":"flysnow_org"});
通过上面的源代码,我们也可以发现,如果我们没有传递callback
对应的回调函数,它就会调用c.Render(code, render.JSON{Data: obj})
,和我们直接使用c.JSON
方法是一样的,直接输出JSON字符串。
JSONP劫持
JSONP的方法其实是不推荐的,因为它的安全性是个问题,也就是JSONP劫持。JSONP劫持其实是JSON劫持的一部分,JSON劫持是包含JSONP劫持的。
JSON劫持还会稍微麻烦一些,因为需要数据是JSON数组,并且要重写JS Array的构建函数;而JSONP,因为有现成的JS回调函数,直接重写JS回调函数就可以了,JSONP让JSON劫持更简单了。
JSON劫持
上面我们介绍了JSON劫持,那么我们如何避免它呢?其实现在的浏览器基本上修复了这个问题,但是我们这里介绍下Gin防止JSON劫持的策略。
JSON劫持,其实就是恶意网站,通过<script>
标签获取你的JSON数据,因为JSON数组默认为是可执行的JS,所以通过这种方式,可以获得你的敏感数据,当然条件还有很多,可以Google JSON劫持详细了解。
对于Gin,解决JSON劫持的方法很简单:
|
|
运行访问http://localhost:8080/secureJson
会发现如下信息:
while(1);["1","2","3"]
最前面有个while(1);
前缀,这就可以在<script>
标签执行我们返回的数据时,就可以无限循环,阻止后面数组数据的执行,防止数据被劫持。
Gin默认的防JSON劫持的前缀是while(1);
,我们可以改变,通过r.SecureJsonPrefix
方法设置即可,如:
|
|
据说Google采用的是while
的方法,facebook采用的是for
的方法。
小结
虽然Gin对JSONP提供了很好的支持,但是我们并不推荐使用,因为JSONP劫持问题,如果要跨域还是使用代理或者CORS比较好.
精彩文章推荐
Golang Gin 实战(六)| 获取Form表单参数和原理分析
Golang Gin 实战(四)| URL查询参数的获取和原理分析
Golang Gin 实战(二)| 简便的Restful API 实现
我有几个的Go语言交流微信群,可以扫码关注公众号flysnow_org
或者网站 https://www.flysnow.org/,加我好友,我拉你进来。