使用Golang开发微信公众平台-接入验证

今年我涉猎的领域有些“广泛”,并且有那么一点“跳跃”:从上半年的终端(游戏)开发到下半年golangdocker以及目前将要提及的微信公众平台 接口开发,似乎有些远离了老本行C以及技术管理的内容。但在这个转型以及创新驱动的时代,这显然是顺势而为。寻求与新兴领域的主动接轨,在实打实的实践 中,扩大了自己的视野,并可以进一步甄别发现适合自己的领域。

移动互联网时代,微信平台一枝独秀,是社交领域的巨人,但其诞生也才不到4年。微信平台的发展前景十分广阔,企鹅公司将其打造为人与人、人与物、物与物的统一、万能入口之雄心不变,因此围绕微信平台广大开发者依旧有诸多机会。

微信公众平台接口应该算是微信平台首批对外开放的接口吧。公众平台相对成熟,但其业务模式依旧在演进和创新。公众平台接口的开发并非不难,上手几个月就可 以写成一本诸如“微信公众平台应用开发实践”的事情就发生在你我眼前,因此这里后续有关微信公众平台接口开发的文章也都是一些入门级的,我个人也是边学 习,边实践,边记录,边分享,就像上半年写Cocos2d-x文章那样。

一、公众号申请(可选

本着“再小的个体,也有自己的品牌”的微信公众平台产品哲学,只要你是合法自然人类,你就可以到https://mp.weixin.qq.com/上申请一个公众号,一般对于个体而言,只能申请订阅号。

对于具有开发能力的订阅号拥有者,你可以在订阅号的“开发者中心”,启用开发者账号。并且“一旦启用并设置服务器配置后,用户发给公众号的消息以及开发者需要的事件推送,将被微信转发到该URL中”。

不过此时即便你填写相关信息并提交,你也不会通过验证。这正是本篇要告诉你的事情,如何写程序实现微信公众平台的接入验证,后续道来。

二、测试号申请(可选)

正式的订阅号申请有些繁琐,需要提交个人信息,需要审核,不会立即生效。并且未认证的订阅号所能使用的功能接口有限(只能使用普通消息接口),而认证又需 要一笔费用(现价300rmb/次)。对于学习者而言,也许真的没有必要。于是我们在学习开发的过程中可以申请测试号来替代真正的公众号。

测试号是一种体验账号,有效期一年,具有各种功能接口体验权限。测试号可以在http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login下申请,申请时有一个类似公众号开发者配置的页面需要你填写服务器配置。同样,你需要进行接入验证(后续道来)。

一旦申请成功,可以用终端微信app扫一扫测试号的QR,关注微信平台测试号,用于后续平台接口开发测试。

以上公众号和测试号二选一

三、公众号服务器

为何需要公众号服务器,这就要谈及微信公众平台的架构了。

很多人觉得微信公众平台的业务模式有些类似于若干年前火爆一时的短信增值业务模式-SP/CP模式:

【终端用户】 <—-短信—> 【移动运营商移动增值业务网关】 <—-> 【SP/CP服务器】

微信公众号时代,这个业务模式变成了:

【终端用户】 <—-微信消息—> 【微信公众平台】 <—–> 【公众号服务器】

短信变成了微信,SP/CP变成了公众号。微信公众平台将终端用户发给公众号的信息转发至公众号配置的公众号服务器URL,公众号服务器做业务处理后,将响应信息通过微信公众平台再发给终端用户。因此我们需要实现公众号业务逻辑的公众号服务器。

本文标题里所说的“接入验证”,指的就是微信公众平台对公众号服务器提供服务的URL有效性的验证。我们在填写开发者中心的“接口配置信 息”并提交时,微信公众平台会向配置的公众号服务器的URL发送验证Request,只有公众号服务存在,且按要求返回包含特定信息的Response, 我们才能真正通过微信公众平台的验证,“接口配置信息”才真正生效。

因此我们需要一台放在公网的主机。如果采用Golang开发公众号服务的话,这样的主机只能是独立的VPS,像国内新浪提供的app engine主机不能运行Golang,无法满足要求(当然如果你使用其他语言开发的话,比如PHP,那么可用的主机范围就很广泛了)。

这里建议申请一个亚马逊免费EC2主机(t2.micro型,免费一年,学习够用)用作学习测试使用或者购买像LinodeDigitalOcean的VPS。关于如何申请亚马逊主机可以咨询谷歌和度娘,这里不赘述。

注意:Amazon EC2实例默认采用的时动态IP,instance重启后IP会发生变化。因此可申请分配一个Elastic IP,并绑定在你的EC2实例上,目前绑定instance的Elastic IP是免费的,这个IP在instance重启后不会变更。当你EC2主机到期后,记得释放这个IP,否则就收费了。

四、接入验证逻辑

前面提到过,无论是公众号还是测试号,当你提交配置URL时会收到提交失败的信息,这是微信公众平台接入验证失败所致。在公众平台开发者文档中,关于URL验证逻辑如下:

开发者提交信息(包括URL、Token)后,微信服务器将发送Http Get请求到填写的URL上,GET请求携带四个参数:signature、timestamp、nonce和echostr。公众号服务程序应该按如下要求进行接入验证:

1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 将加密后获得的字符串与signature对比,如果一致,说明该请求来源于微信
4. 如果请求来自于微信,则原样返回echostr参数内容

以上完成后,接入验证就会生效,开发者配置提交就会成功。

列出Http抓包分析后的文本,理解起来就更容易些:

微信服务器发出的验证Request如下:

GET /?signature=d01007dcff994c555bc51d22e154956ccdc61ec5&timestamp=1418970951&nonce=484765335&echostr=qwe1235 HTTP/1.0\r\n
User-Agent: Mozilla/4.0\r\n
Accept: */*\r\n
Host: wechat.tonybai.com\r\n
Pragma: no-cache\r\n
Content-Length: 0\r\n

应答返回如下:
HTTP/1.0 200 OK\r\n
Date: Fri, 19 Dec 2014 06:35:59 GMT\r\n
Content-Length: nn\r\n
Content-Type: text/plain; charset=utf-8\r\n

qwe1235

五、参考实现

环境:AWS t2.micro ubuntu 14.04 x86_64 Server
     go 1.4

Go语言标准库提供了一个强大的http server,我们直接利用这个server来处理微信平台的Url验证请求。另外微信平台发给公众平台服务器的http request都是请求到"/"下的,这样我们的service无需设置太多http route。

//urlvalidation.go
package main

import (
        "crypto/sha1"
        "fmt"
        "io"
        "log"
        "net/http"
        "sort"
        "strings"
)

const (
        token = "wechat4go"
)

func makeSignature(timestamp, nonce string) string {
        sl := []string{token, timestamp, nonce}
        sort.Strings(sl)
        s := sha1.New()
        io.WriteString(s, strings.Join(sl, ""))
        return fmt.Sprintf("%x", s.Sum(nil))
}

func validateUrl(w http.ResponseWriter, r *http.Request) bool {
        timestamp := strings.Join(r.Form["timestamp"], "")
        nonce := strings.Join(r.Form["nonce"], "")
        signatureGen := makeSignature(timestamp, nonce)

        signatureIn := strings.Join(r.Form["signature"], "")
        if signatureGen != signatureIn {
                return false
        }
        echostr := strings.Join(r.Form["echostr"], "")
        fmt.Fprintf(w, echostr)
        return true
}

func procRequest(w http.ResponseWriter, r *http.Request) {
        r.ParseForm()
        if !validateUrl(w, r) {
                log.Println("Wechat Service: this http request is not from Wechat platform!")
                return
        }
        log.Println("Wechat Service: validateUrl Ok!")
}

func main() {
        log.Println("Wechat Service: Start!")
        http.HandleFunc("/", procRequest)
        err := http.ListenAndServe(":80", nil)
        if err != nil {
                log.Fatal("Wechat Service: ListenAndServe failed, ", err)
        }
        log.Println("Wechat Service: Stop!")
}

编译这个go源码,执行urlvalidation。

$> urlvalidation
2014/12/18 17:48:10 Wechat Service: Start!
2014/12/18 17:48:10 Wechat Service: ListenAndServe failed, listen tcp :80: bind: permission denied

程序提示没有权限绑定80端口。80端口只有管理员权限才能绑定,因此我们需要通过sudo方式执行validation。

$ sudo ./urlvalidation
2014/12/18 09:56:29 Wechat Service: Start!

接下来我们回到订阅号开发者中心配置页面或测试号服务器配置页面,点击提交。在我们的公众号服务器后台可以看到如下日志:

2014/12/18 09:56:52 Wechat Service: validateUrl Ok!

同时你的提交也会显示成功,Url已经验证通过,你将正式成为开发者。

如果我们随意构造一个http get 请求发给validate程序,比如:

curl -s http://wechat.tonybai.com(比如我的URL为http://wechat.tonybai.com)

那么我们将看到validation输出如下错误日志:

2014/12/18 10:02:07 Wechat Service: this http request is not from Wechat platform!

以上源码文件在这里可以下载。

处于安全考虑,后续订阅号平台均需要对收到的http request进行验证,以确保请求来源于微信公众平台。

将Blog迁移到DigitalOcean的VPS上

自从2012年初将Blog从Blogbus搬出来放到同事代理的虚拟主机上后,Blog运行一直很稳定,我也算 是比较满意。但同事的主机代理生意这两年来每况愈下,这促使他在前些时候做出了在今年年末放弃这门生意的决定,于是我又不得不为Blog另找落脚儿地了。

这次不想再单纯的买Wordpress虚拟主机了,一来功能有限,二来国外的入门级VPS价格已经与虚拟主机价格逐渐缩小,尤其是像 DigitalOcean这样的后起之秀,5$/mon的入门级配置VPS基本可以满足我的应用。于是DigitalOcean VPS就成为了我的购买目标。DigitalOcean这两年推广力度大,其Promo code的优惠有时可达20$以上,去年黑色星期五当天就给出了50$的优惠码。于是我期望着今天(2014黑色星期五)DigitalOcean的 50$优惠码能再现江湖。

但事与愿违,当时间走入美国当地时间星期五后,网上哪些所谓50$的Promo code依旧无法正常使用。无奈只能退而求次,使用"SHIPITFAST10"这个10$的优惠码,对于入门级VPS来说,10$也够试用两个月的了。

Digital Ocean VPS的注册和购买流程非常简单,按照官方提示一步一步做即可。这里要注意的是如果选择信用卡支付,务必一次填对信用卡信息,否则account就会短暂 无法使用,你需要fill out一个Form,提交给客服人工验证才能解除对你account的封锁。

接下来就是稍详细的说明Wordpress blog迁移到Digital Ocean VPS的步骤了,希望能对大家有所帮助。

一、备份WordPress Blog

网上关于迁移WordPress的方法有许多方案,之前在测试将WordPress迁移到Docker容器中时,我采用的是数据表导出导入+WordPress程序覆盖的方式,这次我依旧采用此方法。

现有的Blog用的是DirectAdmin的后台管理面板,支持全站备份,备份后的文件为:backup-Nov-27-2014-1.tar.gz。这个压缩包中有两个重要的组件(解压后你就可以看到):

    – backup/tonybai_db.sql
    – domains/tonybai.com/public_html/

   
我们要迁移的就是这两个组件。第一个.sql文件就是我们导出的数据库表,需要导入到新主机中的新库中。而第二个则是Wordpress安装后的文件集合,用于直接覆盖目标主机上对应的Wordpress文件包的。

二、创建Digital Ocean VPS Droplet

在填写完信用卡,利用优惠码充值账户成功后,就可以创建Droplet了。Droplet是DO的术语,理解成一个VPS实例即可。Droplet的创建 体验不错,DO已经准备好了各种VPS常用的应用组合以及OS供选择。我选择了5$/mon的Ubuntu 14.04 x64 + WordPress的组合,机房选择San Francisco 1。确认后,DO会开始创建Droplet操作,不到1分钟,Droplet就创建完毕了。如果不用ssh key,则VPS的root密码会发到你的注册邮箱中。有了root和密码,我们就可以通过"ssh root@YOUR_VPS_IP"访问你的VPS了。

首次后台登陆VPS,VPS会强制你修改root登陆密码。

三、初始安装WordPress

现在我们的VPS上已经安装好了WordPress运行所需要的所有软件了,包括apache2、mysql等。修改/etc/hosts,将自己的域名tonybai.com映射为VPS IP。

访问tonybai.com,WordPress的自安装程序启动,按照提示一步一步即可安装好Wordpress,这里带的Wordpress是4.0.0版本(注意:我们后续是要覆盖掉这个 WordPress的)。

安装好后,再访问tonybai.com就可以看到默认安装后的一篇example blog了。

现在我们进入tonybai.com/wp-admin页面,Apache弹出一个登陆框,在DO官方文档提到过,/wp-admin初始情况使用了 apache的.htaccess credential保护机制了,我们需要输入用户名密码才能进入wp-admin页面。这个用户名密码就在/root/WORDPRESS里。

四、导表

接下来,我们先将backup/tonybai_db.sql导入mysql数据库。

mysql的数据库访问密码在/root/.my.cnf中,用户名是root。

管理mysql我们更多使用phpmyadmin工具,于是通过apt-get install phpmyadmin -y安装一个。

为了通过Web页面访问到phpmyadmin,我们还需执行以下两个步骤:

 在/etc/apache2/apache2.conf尾部添加一行:
        Include /etc/phpmyadmin/apache.conf

 重启apache2:service apache2 restart

之后通过tonybai.com/phpmyadmin访问phpmyadmin工具。登录时使用mysql的root和密码即可。

进入phpmyadmin后,我们可以看到前面的Wordpress安装过程在mysql中建立了名为wordpress的数据库以及名为 wordpress的数据库用户。但我之前的blog使用的数据库用户和数据库并非wordpress,而是tonybai_user和tonybaidb,于是我们需要自己创建 tonybaidb数据库以及tonybai_user这个数据库账号。

创建tonybaidb时,注意使用utf8_general_ci字符集。

创建tonybai_user数据库账户时,注意其权限仅局限于localhost发起的访问以及tonybaidb这个数据库,其密码设置为原blog wp-config.php中的数据库密码。

由于phpmyadmin导入的文件不能超过2M,因此我们只能通过后台导表:

    mysql -u root -p
    mysql> use tonybai_db
    database changed
    mysql> source ./tonybai_db.sql

五、替换Wordpress安装文件

默认下wordpress安装到了/var/www下。我们需要将domains/tonybai.com/public_html替换掉/var/www目录:

cd /var
mv www www.bak

将domain/tonybai.com/public_html cp到/var/下,改名为www

chown -R www-data www
chgrp -R www-data www

剩下的就是访问tonybai.com即可。

是不是熟悉的页面和风格又展现在你眼前了!

六、创建SnapShot

DO提供两种备份方式Snapshot和Backups,其中Snapshot目前还是免费的,但backup服务是要付费的。Snapshot创建的前提是先stop这个Droplet。建议导入blog、访问正常后,马上建立一个Droplet的Snapshot。

七、其它

由于是入门型VPS,其内存仅有512M,并且默认情况下Ubuntu 14.04 VPS没有创建Swap,考虑到VPS的高可用性,我们还是需要自己动手创建一些swap空间,以供不时之需,创建步骤很简单,执行下面命令即可:

fallocate -l 512M /swapfile
mkswap /swapfile
swapon /swapfile

swapon -s  查看一下当前swap,可以看到:
Filename                                Type            Size    Used    Priority
/swapfile                               file            524284  0       -1

另外调试过程中发现访问tonybai.com/feed出现如下错误:
Forbidden:
    You don't have permission to access /feed/ on this server.

Google、Baidu许久才发现真正问题所在:我的旧Blog目录下有一个feed子目录,把这个目录删除即可。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats