借助cryptoJS , jsonp跨域,请求心知天气API ,获取天气信息

来源:互联网 发布:代谢综合征基因组数据 编辑:程序博客网 时间:2024/06/03 05:48
  1. 介绍jsonp的原理
  2. 介绍ajax异步使用jsonp
  3. 通过加密方式获取心知天气数据
  4. 介绍node使用cryptoJS 方式加密数据
  5. 介绍在web前端使用cryptoJS 去加密数据
  6. 具体介绍了借助ajax的jsonp方式获取心知天气数据的过程
  7. 参考文章

之前模仿百度首页,在获取天气信息这一块,比较好奇,就准备尝试着做一下。这一做牵扯出了很多的知识点,于是我在这里记录下来,方便自己,方便别人,也帮心知天气宣传一下,他们的服务做得真的很好http://www.thinkpage.cn/。


打开上面心知数据的官网,有一个V3版本的开发文档。http://www.thinkpage.cn/doc
首先会有我的信息,这部分包含我的API密钥,我的用户ID。这部分内容要等用户注册登录之后,才能看到具体的信息。




接着,往下一翻发现有一句如何使用JSONP方式调用。
工作中Json一直在用,这里看到JSONP,好家伙听过没玩过,一下子好奇心来了,反正没啥事,就研究呗。


于是开启第一部分:

1、jsonp的原理

先说概念:jsonp是动态跨域获取数据的一种方式。
接着我们来说说jsonp的实现。
网上对jsonp的解释有很多,我单纯的将其原理列出,如果诸位看官看的不明白,可以点击文章结尾处的参考文档,或者点击本小节结束时的文档,博采众长,进一步学习这部分知识。
进入正题。
1.1、我们都知道在HTML页面中,由于跨域规则的限制,我们通过ajax无法请求存在其他域中的普通文件。
1.2、但是我们又发现,web页面上引入js文件是不受跨域安全机制的影响的。不仅仅如此,所有有src属性的标签,<script>、<img>、<iframe>都可以突破域的限制。
1.3、恰好此时json数据作为一种格式简单,便于理解的数据交换结构,被大家广泛的使用。关键是json被原生的js支持。

下面补充json对象与json字符串互相转换的方法。

字符串转json对象:var obj = str.parseJSON(); //由JSON字符串转换为JSON对象 或者var obj = JSON.parse(str); //由JSON字符串转换为JSON对象json对象转字符串:var last=obj.toJSONString(); //将JSON对象转化为JSON字符或者var last=JSON.stringify(obj); //将JSON对象转化为JSON字符

1.4、因此,如果我们可以联想到,在前端直接获取不同域的服务器上的数据,可以通过向目标服务器直接请求一个动态js文件回来(该文件一般以json方式为后缀),之所以是动态的,是因为服务器可以将数据填充到动态文件中。

1.5、发送请求时,使用<script>标签引入的文件是通过get方式获取的,而get请求的后面就可以带参数,这里的参数就是回调函数的名字。

1.6、服务器接受到请求之后,分离出回调函数,再将前端所需要的数据封装成一个对象,以参数的形式给到回调函数中,接着请求返回到前端。
1.7、返回的动态文件因为在<script>标签中,所以会被立即执行,我们在本地写好的回调函数中就可以对返回的数据做处理了。

注意:以下是重点。
我们在html文件中,已经写好了函数的声明,将函数名跟在跨域获取数据的脚本后面作为参数,然后准被发送到服务器。
发送到服务器是通过构造一个script标签来完成的。例子如下:

 // 得到航班信息查询结果后的回调函数    var flightHandler = function(data){        alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');    };// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)    var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";    // 创建script标签,设置其属性    var script = document.createElement('script');    script.setAttribute('src', url);    // 把script标签加入head,此时调用开始    document.getElementsByTagName('head')[0].appendChild(script); 

当请求到达服务器后,会根据参数,制作一个数据出来。再将这个数据以参数的形式,填充到回调函数的参数中。然后服务器将动态生成的js文件返回。

返回后,回调函数会立即执行。此时我们就达到了跨域从后台获取数据的效果。

2、ajax异步使用jsonp

此外,我们还应该知道的是,jQuery也是支持jsonp的,它是通过$.ajax的方式支持jsonp的。

 $.ajax({             type: "get",             async: false,             url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",             dataType: "jsonp",         cache: true,             jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)             jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据             success: function(json){                 alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。');             },             error: function(){                 alert('fail');             }         });

这里要注意以下几点:
1、jsonp只能以get方式提交。
因为它本身就相当于一个动态添加的script标签,而这种方式发出的请求就是以get方式发出去。所以请求类型为get。
2、cache : true
jQuery会在请求后默认添加一个时间戳,当有了这个时间戳之后,就不会使用缓存。不过在获取心知天气API时,cache这个值可以不设置。
3、请求频率
请求频率要和免费用户的每分钟的次数相匹配,不然会被服务器拒绝服务。
更多内容:请参考http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
这篇文章有例子,讲的也很详细。
知道了跨域的原理,那么我们接着回到心知数据V3版 API这边,来考虑获取数据。


2、通过加密的方式获取数据



3.1、介绍node使用cryptoJS 方式加密数据

API中提到采用node.js的crypto模块加密。
我按照文中的例子做了一个demo,得出的结果与文中结果一致,下面是代码。

var crypto = require("crypto");var str = "ts=1443079775&ttl=30&uid=U123456789";var key = "secret";var result = crypto.createHmac('sha1',key);var final = result.update(str).digest('base64');console.log(final);

当然也有不用node的测试方法。各位看官可以到https://1024tools.com/hmac 网站中验证上面的数据。
这里补充些关于node.js加密的知识点:
首先,如果要使用上述的方法,本地至少有node.js的环境。我测试时用的是6.9.1的版本。

使用require('crypto')调用加密模块
加密模块需要底层系统提供OpenSSL的支持。它提供了一种安全凭证的封装方式,可以用于HTTPS安全网络以及普通HTTP连接。

crypto.createHash(algorithm)
创建并返回一个hash对象,它是一个指定算法的加密hash,用于生成hash摘要。参数algorithm可选择系统上安装的OpenSSL版本所支持的算法。例如:'sha1', 'md5', 'sha256', 'sha512'等。在近期发行的版本中,openssl list-message-digest-algorithms会显示这些可用的摘要算法。

crypto.createHmac(algorithm, key)
创建并返回一个hmac对象,它是一个指定算法和密钥的加密hmac。参数algorithm可选择OpenSSL支持的算法 - 参见上文的createHash。参数key为hmac所使用的密钥。

hash.digest(encoding='binary')  hmac.digest(encoding='binary')

计算所有传入数据的hash摘要。参数encoding(编码方式)可以为'hex', 'binary' 或者'base64'。

hmac.update(data)
更新hmac的内容为指定的data。当使用流数据时可能会多次调用该方法。更多内容请参考:http://www.jb51.net/article/50668.htm


知道了这一步,我们知道加密的流程是什么样了。不知大家是否注意到例子中有一个ts。
这个ts其实就是Unix 的时间戳,是当前时间的非格式化表达。
显然例子中的ts要被替换为当前的时间,这里获取本地时间的方法为:

var myDate = new Date();var ts = myDate.getTime();        //获取当前时间(从1970.1.1开始的毫秒数)
参考文章:http://www.cnblogs.com/carekee/articles/1678041.html

3.2、介绍在web前端使用CryptoJS加密数据

接下来,问题又来了,那么我怎么将加密方式移植到web页面中去呢?

其实呢网上有一个crypto的加密库,它不仅可以在node中使用,也可以在浏览器中以script标签引用的方式使用。下载地址:https://github.com/brix/crypto-js
在这一关,我还是研究了一段时间的。网上有一些方法介绍web端createHmac加密,但是其中<script>引入的用于加密的js文件早已经失效了。所以下面的方法都是我自己摸索出来的。

下面直接写成果,免得大家走弯路。

1、去git上下载crypto-js这个库
下载好后,在crypto-js-develop\src文件夹,我们可以看到所有的与加密相关的文件。
2、在页面中引入cryptoJS的库。
虽然js文件很多,但是我们需要的js文件却并不多。这里你只需要选择与自己加密方式相关的js文件就即可。

<script src="js/core.js"></script><script src="js/cipher-core.js"></script><script src="js/enc-base64.js"></script><script src="js/hmac.js"></script><script src="js/sha1.js"></script>

可以看到除了两个核心模块,其他的就是我们在加密过程中需要模块。那么接下来,我们就可以在web页面中执行hmac的加密了。str是被加密的字符串,key是密钥。

var result = CryptoJS.HmacSHA1(str,key);       var signature = result.toString(CryptoJS.enc.Base64);signature = encodeURI(signature);

4、借助ajax的jsonp方式获取心知天气数据的过程

首先,组装加密后的请求:

 var ts = new Date().getTime(); var str = "ts="+ts+"&ttl=30&uid=U75028BE2C"; //uid需要换成自己账户中的uidvar result = CryptoJS.HmacSHA1(str,key);  //key需要换成自己账户中的keyvar signature = result.toString(CryptoJS.enc.Base64);signature = encodeURI(signature);

接着,组装url:

var url = "https://api.thinkpage.cn/v3/weather/now.json?location="+ city +"&"+str+"&sig="+signature;

然后,ajax发出请求:

$.ajax({            type: "get",            async: false,            cache: false,            url: url,            dataType: "jsonp",            jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)            jsonpCallback:"showWeather",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据            success: function(json){     console.log(json);            },            error: function(){                alert('fail');            }        });


至此,在ajax的回调函数中添加业务逻辑代码,整个jsonp跨域获取数据就ok了,接下来本地测试。心知天气官方需要有域名,并且域名绑定账号才能获取数据,否则连接将一定会被禁掉。

我估计是为了盈利的考虑,一个账号绑定一个域名吧。当然这个地方是我整个文章中唯一不能确认的地方,为什么要这么做。如果各位知道的话,请告诉我。不过需要域名,我就给你一个好了。我们不需要自己搭建一个服务器,或者买一个域名,只需要在本地打一个wamp环境,在host文件中将127.0.0.1映射到例如c.test123.com。然后再将这个域名绑定到你注册心知天气的账号上。


绑定过程类似下面的方式:


最后将工程放在wamp的www目录下运行就可以了。

整个文章,有自己思考的部分,也有借鉴别人的部分。总体上记录了个人思考发现的整个过程。这一次好奇,解决了一个又一个的问题,完全搞懂了jsonp。谢谢各位大牛的分享,也感谢心知天气的客服耐心细致的回答,希望他们的公司越做越好。
参考文章汇总:

http://www.thinkpage.cn/doc
http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
https://my.oschina.net/jack230230/blog/69826
http://blog.csdn.net/uikoo9/article/details/47926903
http://www.cnblogs.com/carekee/articles/1678041.html
http://www.cnblogs.com/worfdream/articles/1956449.html

这编辑器好难用,看来我以后要用MarkDown写博客了。

1 0