EncodingAESKey
关键字:EncodingAESKey
公众平台消息体签名及加解密方案概述
1.新增消息体签名验证,用于公众平台和公众账号验证消息体的正确性
2.针对推送给微信公众账号的普通消息和事件消息,以及推送给设备公众账号的设备消息进行加密
3.公众账号对密文消息的回复也要求加密
开发者需注意,公众账号主动调用API的情况将不受影响。
启用加解密功能(即选择兼容模式或安全模式)后,公众平台服务器在向公众账号服务器配置地址(可在“开发者中心”修改)推送消息时,URL将新增加两个参数(加密类型和消息体签名),并以此来体现新功能。加密算法采用AES,具体的加解密流程和方案请看接入指引、技术方案和示例代码。
为了配合消息加密功能的上线,并帮助开发者适配新特性,公众平台提供了3种加解密的模式供开发者选择,即明文模式、兼容模式、安全模式(可在“开发者中心”选择相应模式),选择兼容模式和安全模式前,需在开发者中心填写消息加解密密钥EncodingAESKey。
明文模式:维持现有模式,没有适配加解密新特性,消息体明文收发,默认设置为明文模式
兼容模式:公众平台发送消息内容将同时包括明文和密文,消息包长度增加到原来的3倍左右;公众号回复明文或密文均可,不影响现有消息收发;开发者可在此模式下进行调试
安全模式(推荐):公众平台发送消息体的内容只含有密文,公众账号回复的消息体也为密文,建议开发者在调试成功后使用此模式收发消息
什么是EncodingAESKey? 微信公众平台采用AES对称加密算法对推送给公众帐号的消息体对行加密,EncodingAESKey则是加密所用的秘钥。公众帐号用此秘钥对收到的密文消息体进行解密,回复消息体也用此秘钥加密。
此外,微信公众平台为开发者提供了5种语言的示例代码(包括C++、php、Java、Python和C#版本,http://mp.weixin.qq.com/wiki/downloads/SampleCode.zip )。 请开发者查看接入指引和开发者FAQ来接入消息体签名及加解密功能,若关注技术实现,可查看技术方案。
该文档讲述如何使用示例代码接入加解密,参考本文档并使用示例代码,加解密的接入将非常简单。若想进一步的了解细节,请查看技术方案。 微信公众平台提供了C++、php、Java、Python和C# 5种语言的示例代码,每种语言的类名和接口名均一致,下面以C++为例说明:
目录[隐藏] |
函数说明
构造函数
// @param sToken: 公众平台上,开发者设置的Token
// @param sEncodingAESKey: 公众平台上,开发者设置的EncodingAESKey
// @param sAppid: 公众号的appid
WXBizMsgCrypt(const std::string &sToken,
const std::string &sEncodingAESKey,
const std::string &sAppid);
解密函数
// 检验消息的真实性,并且获取解密后的明文
// @param sMsgSignature: 签名串,对应URL参数的msg_signature
// @param sTimeStamp: 时间戳,对应URL参数的timestamp
// @param sNonce: 随机串,对应URL参数的nonce
// @param sPostData: 密文,对应POST请求的数据
// @param sMsg: 解密后的明文,当return返回0时有效
// @return: 成功0,失败返回对应的错误码
int DecryptMsg(const std::string &sMsgSignature,
const std::string &sTimeStamp,
const std::string &sNonce,
const std::string &sPostData,
std::string &sMsg);
加密函数
//将公众号回复用户的消息加密打包
// @param sReplyMsg:公众号待回复用户的消息,xml格式的字符串
// @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp
// @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
// @param sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,当return返回0时有效
// return:成功0,失败返回对应的错误码
int EncryptMsg(const std::string &sReplyMsg,
const std::string &sTimeStamp,
const std::string &sNonce,
std::string &sEncryptMsg);
使用方法
在安全模式或兼容模式下,url上会新增两个参数encrypt_type和msg_signature。encrypt_type表示加密类型,msg_signature:表示对消息体的签名。 url上无encrypt_type参数或者其值为raw时表示为不加密;encrypt_type为aes时,表示aes加密(暂时只有raw和aes两种值)。公众帐号开发者根据此参数来判断微信公众平台发送的消息是否加密。
兼容模式和安全模式加解密的方法完全一样,兼容模式的xml消息体比安全模式多了几个明文字段,具体请查看《消息加解密详细技术方案》。
实例化对象
使用构造函数,实例化一个对象,传入公众帐号的token, appid, EncodingAESKey。
解密
安全模式或者兼容模式下,公众号收到以下带密文消息体(“……”表示兼容模式下的明文字段):
encrypt_msg =
<xml>
<ToUserName><![CDATA[gh_10f6c3c3ac5a]]></ToUserName>
……
<Encrypt><![CDATA[hQM/NS0ujPGbF+/8yVe61E3mUVWVO1izRlZdyv26zrVUSE3zUEBdcXITxjbjiHH38kexVdpQLCnRfbrqny1yGvgqqKTGKxJWWQ9D5WiiUKxavHRNzYVzAjYkp7esNGy7HJcl/P3BGarQF3+AWyNQ5w7xax5GbOwiXD54yri7xmNMHBOHapDzBslbnTFiEy+8sjSl4asNbn2+ZVBpqGsyKDv0ZG+DlSlXlW+gNPVLP+YxeUhJcyfp91qoa0FJagRNlkNul4mGz+sZXJs0WF7lPx6lslDGW3J66crvIIx/klpl0oa/tC6n/9c8OFQ9pp8hrLq7B9EaAGFlIyz5UhVLiWPN97JkL6JCfxVooVMEKcKRrrlRDGe8RWVM3EW/nxk9Ic37lYY5j97YZfq375AoTBdGDtoPFZsvv3Upyut1i6G0JRogUsMPlyZl9B8Pl/wcA7k7i4LYMr2yK4SxNFrBUw==]]></Encrypt>
</xml>
调用DecryptMsg接口,传入收到的url上的参数:msg_signature(注意:不是signature,而是msg_signature), timestamp, nonce和encrypt_msg,若调用成功,sMsg则为输出结果,其内容为如下的明文的xml消息体:
<xml>
<ToUserName><![CDATA[gh_10f6c3c3ac5a]]></ToUserName>
<FromUserName><![CDATA[oyORnuP8q7ou2gfYjqLzSIWZf0rs]]></FromUserName>
<CreateTime>1411035097</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test message]]></Content>
<MsgId>6060349595123187712</MsgId>
</xml>
公众帐号处理消息
生成需要回复给微信公众平台的xml消息体,假设回复以下内容:
res_msg =
<xml>
<ToUserName><![CDATA[oyORnuP8q7ou2gfYjqLzSIWZf0rs]]></ToUserName>
<FromUserName><![CDATA[gh_10f6c3c3ac5a]]></FromUserName>
<CreateTime>1411034505</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[Welcome to join us!]]></Content>
<FuncFlag>0</FuncFlag>
</xml>
回包加密
调用EncryptMsg接口,传入需要回复给微信公众平台的res_msg, timestamp, nonce,若加密成功,则sEncryptMsg为密文消息体,内容如下:
<xml>
<Encrypt><![CDATA[LDFAmKFr7U/RMmwRbsR676wjym90byw7+hhh226e8bu6KVYy00HheIsVER4eMgz/VBtofSaeXXQBz6fVdkN2CzBUaTtjJeTCXEIDfTBNxpw/QRLGLqqMZHA3I+JiBxrrSzd2yXuXst7TdkVgY4lZEHQcWk85x1niT79XLaWQog+OnBV31eZbXGPPv8dZciKqGo0meTYi+fkMEJdyS8OE7NjO79vpIyIw7hMBtEXPBK/tJGN5m5SoAS6I4rRZ8Zl8umKxXqgr7N8ZOs6DB9tokpvSl9wT9T3E62rufaKP5EL1imJUd1pngxy09EP24O8Th4bCrdUcZpJio2l11vE6bWK2s5WrLuO0cKY2GP2unQ4fDxh0L4ePmNOVFJwp9Hyvd0BAsleXA4jWeOMw5nH3Vn49/Q/ZAQ2HN3dB0bMA+6KJYLvIzTz/Iz6vEjk8ZkK+AbhW5eldnyRDXP/OWfZH2P3WQZUwc/G/LGmS3ekqMwQThhS2Eg5t4yHv0mAIei07Lknip8nnwgEeF4R9hOGutE9ETsGG4CP1LHTQ4fgYchOMfB3wANOjIt9xendbhHbu51Z4OKnA0F+MlgZomiqweT1v/+LUxcsFAZ1J+Vtt0FQXElDKg+YyQnRCiLl3I+GJ/cxSj86XwClZC3NNhAkVU11SvxcXEYh9smckV/qRP2Acsvdls0UqZVWnPtzgx8hc8QBZaeH+JeiaPQD88frNvA==]]></Encrypt>
<MsgSignature><![CDATA[8d9521e63f84b2cd2e0daa124eb7eb0c34b6204a]]></MsgSignature>
<TimeStamp>1411034505</TimeStamp>
<Nonce><![CDATA[1351554359]]></Nonce>
</xml>
注意事项
- EncodingAESKey长度固定为43个字符,从a-z,A-Z,0-9共62个字符中选取。 公众帐号可以在公众平台的开发者中心的服务器配置修改
- 出于安全考虑,公众平台网站提供了修改EncodingAESKey的功能(在EncodingAESKey可能泄漏时进行修改),所以建议公众账号保存当前的和上一次的EncodinAESKey,若当前EncodingAESKey解密失败,则尝试用上一次的EncodingAESKey的解密。回包时,用哪个Key解密成功,则用此Key加密对应的回包
- 兼容模式消息体同时存在明文和密文,消息体会增至以前的3倍左右,开发者注意检查系统,防止因消息变长和URL参数增加而出现接收错误
- 如果url上无encrypt_type参数或者其值为raw,则回复明文,否则回复密文。兼容模式期间公众账号回复明文或密文均可(不要两种类型都回)
函数错误返回码
函数返回码 | 说明 |
---|---|
0 | 处理成功 |
-40001 | 校验签名失败 |
-40002 | 解析xml失败 |
-40003 | 计算签名失败 |
-40004 | 不合法的AESKey |
-40005 | 校验AppID失败 |
-40006 | AES加密失败 |
-40007 | AES解密失败 |
-40008 | 公众平台发送的xml不合法 |
-40009 | Base64编码失败 |
-40010 | Base64解码失败 |
-40011 | 公众帐号生成回包xml失败 |
示例代码下载
微信公众平台为开发者提供了5种语言的示例代码(包括C++、php、Java、Python和C#版本) 点击下载 http://mp.weixin.qq.com/wiki/downloads/SampleCode.zip
阅读须知
1. EncodingAESKey长度固定为43个字符,从a-z,A-Z,0-9共62个字符中选取,公众帐号可以在公众平台的开发者中心的服务器配置修改;
2. AES密钥:AESKey=Base64_Decode(EncodingAESKey + “=”),EncodingAESKey尾部填充一个字符的“=”, 用Base64_Decode生成32个字节的AESKey;
3. AES采用CBC模式,秘钥长度为32个字节,数据采用PKCS#7填充;PKCS#7:K为秘钥字节数(采用32),buf为待加密的内容,N为其字节数。Buf需要被填充为K的整数倍。在buf的尾部填充(K-N%K)个字节,每个字节的内容是(K- N%K);
尾部填充 | |
---|---|
01 | if ( N%K==(K-1)) |
0202 | if ( N%K==(K-2)) |
030303 | if ( N%K==(K-3)) |
... | ... |
KK....KK (K个字节) | if ( N%K==0) |
具体详见:http://tools.ietf.org/html/rfc2315
5. 出于安全考虑,公众平台网站提供了修改EncodingAESKey的功能(在EncodingAESKey可能泄漏时进行修改),所以建议公众账号保存当前的和上一次的EncodingAESKey,若当前EncodingAESKey生成的AESKey解密失败,则尝试用上一次的AESKey的解密。回包时,用哪个AESKey解密成功,则用此AESKey加密对应的回包;
6. 兼容模式消息体同时存在明文和密文,消息体会增至以前的3倍左右,开发者注意检查系统,防止因消息变长和URL参数增加而出现接收错误;
7. 微信团队提供了多种语言的示例代码(包括php、Java、C++、Python、C#),请开发者尽量使用示例代码。(http://mp.weixin.qq.com/wiki/downloads/SampleCode.zip )
下面以普通文本消息为例,详细说明公众平台对消息体加解密的方法和流程,其它普通消息和事件消息的加解密可以此类推。
公众账号接收用户消息
消息体加密
现有消息为明文,格式如下:
msg =
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
兼容模式期间同时保留明文和密文,消息格式如下:
new_msg=
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
<Encrypt><![CDATA[msg_encrypt]]</Encrypt>
</xml>
安全模式下,消息体只有密文,格式如下:
new_msg=
<xml>
<ToUserName><![CDATA[toUser]]</ToUserName>
<Encrypt><![CDATA[msg_encrypt]]</Encrypt>
</xml>
其中,msg_encrypt = Base64_Encode( AES_Encrypt[ random(16B) + msg_len(4B) + msg + $AppId] )
AES加密的buf由16个字节的随机字符串、4个字节的msg_len(网络字节序)、msg和$AppId组成,其中msg_len为msg的长度,$AppId为公众帐号的AppId
AESKey =Base64_Decode(EncodingAESKey + “=”),32个字节
url上增加参数encrypt_type,encrypt_type的值为raw时表示为不加密,encrypt_type的值为aes时,表示aes加密(暂时只有raw和aes两种值),无encrypt_type参数同样表示不加密
消息体签名
为了验证消息体的合法性,公众平台新增消息体签名,开发者可用以验证消息体的真实性,并对验证通过的消息体进行解密
在url上增加参数:msg_signature
msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))
参数 | 说明 |
---|---|
Token | 公众平台上,开发者设置的Token |
timestamp | URL上原有参数,时间戳 |
nonce | URL上原有参数,随机数 |
msg_encrypt | 前文描述密文消息体 |
消息体验证和解密
开发者先验证消息体签名的正确性,验证通过后,再对消息体进行解密。
验证方式
1. 开发者计算签名,dev_msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))
2. 比较dev_msg_signature和URL上带的msg_signature是否相等,相等则表示验证通过
解密方式如下:
1. aes_msg=Base64_Decode(msg_encrypt)
2. rand_msg=AES_Decrypt(aes_msg)
3. 验证尾部$AppId是否是自己的AppId,相同则表示消息没有被篡改,这里进一步加强了消息签名验证
4. 去掉rand_msg头部的16个随机字节,4个字节的msg_len,和尾部的$AppId即为最终的xml消息体
公众账号向用户回复消息
如果url上无encrypt_type或者其值为raw,则回复明文,否则按照上述的加密算法加密回复密文。兼容模式期间公众账号回复明文或密文均可(不要两种类型都回)
回复消息体的签名与加密
现有消息格式:
msg=
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
加密后消息格式:
new_msg=
<xml>
<Encrypt><![CDATA[msg_encrypt]]></Encrypt>
<MsgSignature><![CDATA[msg_signature]]></MsgSignature>
<TimeStamp>timestamp</TimeStamp>
<Nonce><![CDATA[nonce]]></Nonce>
</xml>
其中,msg_encrypt=Base64_Encode(AES_Encrypt [random(16B)+ msg_len(4B) + msg + $AppId])
random(16B)为16字节的随机字符串;msg_len为msg长度,占4个字节(网络字节序),$AppId为公众账号的AppId
AESKey =Base64_Decode(EncodingAESKey + “=”),32个字节
msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))
timestamp、nonce回填请求中的值或者重新生成均可
消息加解密功能开发者FAQ
Q 为什么要上线消息加密功能?
A 为了更好的保护用户和公众账号的信息安全。
Q 接入消息加解密功能复杂吗?
A 开发者接入消息加解密功能并不复杂,微信团队提供了5种语言的示例代码(包括C++、php、Python、Java和C#),对于使用这个5种语言的开发者,只需根据《消息加解密接入指引》,参考示例代码,调用微信公众平台提供的函数即可;而对于其他语言的开发者,需根据《消息加解密详细技术方案》编写相关代码。
Q 消息加密功能将带来哪些重要变化?
A 有如下几个方面:
- 选择明文模式时,收发消息的方式和原先相同,但安全系数较低,微信团队推荐开发者在兼容模式下开发调试,并升级到安全模式;
- 选择兼容模式时,消息包同时包括明文和密文,消息包的长度会相应增加到原来的3倍左右,开发者需检查系统,做好预留,防止因消息变长而接收出错;
- 兼容模式和安全模式下,公众平台服务器向公众账号服务器配置地址URL推送消息时,将会增加两个参数;
- 安全模式下,内容为纯密文,请提前做好接收消息的解密工作和回复消息的加密工作。
Q 什么是EncodingAESKey?
A 微信公众平台采用AES对称加密算法对推送给公众帐号的消息体对行加密,EncodingAESKey则是加密所用的秘钥。公众帐号用此秘钥对收到的密文消息体进行解密,回复消息体也用此秘钥加密。
Q 开发者如何判断消息是否被加密?什么情况下需要对回包进行加密?
A 请开发者根据URL参数来判断:url上无encrypt_type参数或者其值为raw,表示消息体仅含有明文,公众账号回复明文。encrypt_type为aes则表示消息体含有密文,公众账号回复密文(兼容模式期间回复明文或密文均可)。
Q 公众账号开发者上线加解密版本后,还需要保留明文解包和回包逻辑吗?
A 暂时先保留之前的逻辑,根据参数判断,也做成兼容模式比较好。
Q 常见错误举例
A 常见错误原因如:
- xml格式不对:如<TimeStamp>写成了<Timestamp > (s小写了且p和>中间有空格)
- 公众平台网站提供了修改EncodingAESKey的功能,公众账号需要保存当前的和上一次的EncodingAESKey,若当前的EncodingAESKey解密失败,则尝试用上一次的EncodingAESKey解密。回包时,用哪个Key解密成功,则用此Key加密对应的回包
- 调用DecryptMsg解密时,传入的是url上的msg_signature,而不是signature
- Java要求jdk 1.6及1.6以上
- 异常java.security.InvalidKeyException:illegal Key Size的解决方案:在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html 下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt,如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件;如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件
EncodingAESKey的更多相关文章
- 微信消息体加解密及EncodingAESKey
公众平台消息体签名及加解密方案概述 1.新增消息体签名验证,用于公众平台和公众账号验证消息体的正确性 2.针对推送给微信公众账号的普通消息和事件消息,以及推送给设备公众账号的设备消息进行加密 3.公众 ...
- 小程序登录解密用户数据encryptedData -41001: encodingAesKey 非法
问题: 做小程序微信授权登录,先获取code,然后去获取到session_key和open_id,再拿到encryptedData,传到服务器去解密拿到用户信息,但是有时成功,有时返回-41001错误 ...
- 微信公众号平台Url Token EncodingAESKey 注意点
最近公司让我开发微信公众号平台扫码登录,同步用户信息于PC端,所做的过程当中遇到了一些坑,做完了就总结一下需要注意的点,如若大家开发过程中遇到同样的问题,可以借鉴! 第一:配置域名 作用:配置域名为了 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(73)-微信公众平台开发-消息管理
系列目录 前言 回顾上一节,我们熟悉的了解了消息的请求和响应,这一节我们来建立数据库的表,表的设计蛮复杂 你也可以按自己所分析的情形结构来建表 必须非常熟悉表的结果才能运用这张表,这表表的情形涵盖比较 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(71)-微信公众平台开发-公众号管理
系列目录 思维导图 下面我们来看一个思维导图,这样就可以更快了解所需要的功能: 上一节我们利用了一个简单的代码例子,完成了与微信公众号的对话(给公众号发一条信息,并得到回复) 这一节将讲解公众号如何设 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(70)-微信公众平台开发-成为开发者
系列目录 前言: 一.阅读这段系列之前,你必须花半天时间大致阅读微信公众平台的API文档,我尽量以简短快速的语言与大家分享一个过程 二.借助微信公众平台SDK Senparc.Weixin for C ...
- 用java开发微信公众号:公众号接入和access_token管理(二)
本文为原创,原始地址为http://www.cnblogs.com/fengzheng/p/5027630.html 上一篇说了微信开发的准备工作,准备工作完成之后,就要开始步入正题了.其实微信公众号 ...
- 微信企业号开发(1)WebAPI在回调模式中的URL验证
微信回调模式的官方文档. 开发语言:C#(微信相关功能代码可以从官网下载) 首先,必须要明确几个参数,这几个参数在微信企业号中,每次调用都会使用到. 1.msg_signature:签名(已加密,加密 ...
- C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密
在上篇随笔<C#开发微信门户及应用(19)-微信企业号的消息发送(文本.图片.文件.语音.视频.图文消息等)>介绍了有关企业号的消息发送,官方特别声明消息是不用加密发送的.但是在回调的服务 ...
随机推荐
- 8.0 Qweb 报表编写步骤
8.0 采用的是Qweb报表,摒弃了7.0中的RML报表. 1.首先在xml文件中注册一个报表: <report id="qweb_test_report" model=&q ...
- sqlmap我常用到的几个参数
-r "抓的包存放的文件路径.txt" 一般方便带cookie与session类型 --dbms Oracle/Mysql 指定数据库类型 指定变量注入点变量:在变量 ...
- JAVA图片相关
有些图片后缀为jpg,但是实际格式却不是jpg,通过url下载图片字节.然后用ImageIO读取时,出现返回null的情况.出现这种情况,就需要使用webp-imageio.jar.(https:// ...
- poj 3253 初涉二叉堆 模板题
这道题很久以前就做过了 当时是百度学习了优先队列 后来发现其实还有个用sort的办法 就是默认sort排序后 a[i]+=a[i-1] 然后sort(a+i,a+i+n) (大概可以这样...答案忘了 ...
- 1. Programming in C is fun!
Programming in C is fun! #include <stdio.h> int main() { printf("Programming in C is fun! ...
- Moving in Unity
转自:http://angryant.com/2014/03/07/Moving-in-Unity/ ,详细描述了物体在unity中移动的几种方式,并且给出了代码描述,对加深对Unity理解很有帮助, ...
- 20145235 《Java程序设计》第8周学习总结
教材学习内容总结 15.1.1日志API简介 使用日志的起点是logger类,logger实例的创建有许多要处理的要素,必须使用logger的静态方法getLogger(). 通常在哪个类上取得的lo ...
- laravel 外键schema RBAC
$table->bigIncrements('id') ; Incrementing ID (primary key) using a " UNSIGNED BIG INTEGE ...
- scala手动编译运行
1 Person.scala class Person { var name = "" } object Person { // a one-arg constructor de ...
- http://d3js.org/
http://d3js.org/ http://www.ourd3js.com/wordpress/?p=51 http://www.ourd3js.com/wordpress/?p=104file: ...