专栏首页飞雪无情的博客Golang Gin 实战(九)| JSONP跨域和劫持

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函数。

<script type="text/javascript">
function sayHello(data){
    alert(JSON.stringify(data));
}
</script>

以上我们定义了一个JS函数sayHello,可以通过alert的方式,显示对应的data数据。

假设我们通过http://localhost:8080/jsonp?callback=sayHello来调用sayHello函数,那么我们这个URL输出的内容要是这样:

sayHello({"wechat":"flysnow_org"});

并且对应的Content-type设置为application/javascript,这样才能达到调用sayHello函数的目的,也就是通过JSONP的方式解决了数据跨域读取的问题。

最终,我们的网页的源代码是这样的:

<script type="text/javascript">
function sayHello(data){
    alert(JSON.stringify(data));
}
</script>
<script type="text/javascript" src="http://localhost:8080/jsonp?callback=sayHello"></script>

注意前后顺序,必须要先定义sayHello函数。

Gin JSONP 实现

要通过Gin来实现服务端对JSONP的支持非常简单,只需要使用JSONP函数即可。

func main() {
	r := gin.Default()
	r.GET("/jsonp", func(c *gin.Context) {
		c.JSONP(200, gin.H{"wechat": "flysnow_org"})
	})
	r.Run(":8080")
}

它的使用方法和c.JSON一模一样,第一个参数是HTTP Status Code,第二个是返回的数据。

func (c *Context) JSONP(code int, obj interface{}) {
	callback := c.DefaultQuery("callback", "")
	if callback == "" {
		c.Render(code, render.JSON{Data: obj})
		return
	}
	c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
}

通过上面的源代码,我们发现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劫持的方法很简单:

    r := gin.Default()
	a := []string{"1", "2", "3"}
	r.GET("/secureJson", func(c *gin.Context) {
		c.SecureJSON(200, a)
	})

运行访问http://localhost:8080/secureJson会发现如下信息:

while(1);["1","2","3"]

最前面有个while(1);前缀,这就可以在<script>标签执行我们返回的数据时,就可以无限循环,阻止后面数组数据的执行,防止数据被劫持。

Gin默认的防JSON劫持的前缀是while(1);,我们可以改变,通过r.SecureJsonPrefix方法设置即可,如:

r := gin.Default()
r.SecureJsonPrefix("for(;;);")

据说Google采用的是while的方法,facebook采用的是for的方法。

小结

虽然Gin对JSONP提供了很好的支持,但是我们并不推荐使用,因为JSONP劫持问题,如果要跨域还是使用代理或者CORS比较好。更多关于Gin的讨论可以加入我的星球Golang Gin 实战,有更深入的讨论,一对一的答疑,公众号和博客没有的源代码分析。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Go语言golang 200行写区块链源代码分析

    Github上有一个Repo,是一个使用Go语言(golang),不到200行代码些的区块链源代码,准确的说是174行。原作者起了个名字是 Code your ...

    飞雪无情
  • Go语言实战笔记(二十四)| Go 反射

    和Java语言一样,Go也实现运行时反射,这为我们提供一种可以在运行时操作任意类型对象的能力。比如我们可以查看一个接口变量的具体类型,看看一个结构体有多少字段,...

    飞雪无情
  • Android注解支持(Support Annotations)

    原文地址 http://tools.android.com/tech-docs/support-annotations

    飞雪无情
  • 细说RESTful API安全之概述

    目前许多前后端应用都采取REST架构风格,前端应用和后端服务通过API进行数据交换。 通过REST API在网络中进行数据交换时很容易被网络抓包,然后进行恶意批...

    2Simple
  • Java对MySQL数据库进行连接、查询和修改

    http://www.cnblogs.com/aniuer/archive/2012/09/10/2679241.html

    bear_fish
  • 嘿!你收到了一封苹果WWDC 2016的邀请函

    镁客网
  • python网络编程--socket简单

    python网络编程                                                                      ...

    py3study
  • Geth RPC API中文文档

    Geth除了支持官方的DApp API开发接口,还支持额外的管理API接口。类似于DApp API,这些管理API也是通过JSON-PRC协议提供,并且遵循同样...

    用户1408045
  • Python数据类型之列表(后续)

    如图所示,有list1和list2两个列表,我们可以发现,原来列表竟然可以比较大小,在这里肯定有读者会说,123肯定小于234,但是如果我们往列表里面多添加几个...

    王强
  • 如何学python-第三课 基础字符串操作

    在上一篇文章中,我们学习了有关变量和输出的一些基础知识。大家应该还记得在上一篇文章中出现的字符串类型(string)吧!说白了,string类型其实就是一堆单词...

    用户1631416

扫码关注云+社区

领取腾讯云代金券