Awesome
微信小程序示例 - 一笔到底
微信小程序提供了一套在微信上运行小程序的解决方案,有比较完整的框架、组件以及 API,在这个平台上面的想象空间很大。
微信的定位并不是 HTML5,这里很多人都有误解。在一些实现上,并不能想当然地用 HTML5 的思路来思考。比如,微信的请求接口 wx.request
并不支持 cookie
传递,所以会话层不能使用传统的 Session 方式。
这篇文章分享一个简单的画图应用,使用自己新鲜出炉的小程序会话管理能力来判断并当前用户的身份。
小程序非常简单,使用 Canvas 绘图后,把序列化的 actions
提交给服务器保存。下次加载的时候,再列出用户曾经绘制过的图。
部署和运行
拿到了本小程序源码的朋友可以尝试自己运行起来。
整体架构
1. 准备域名和证书
在微信小程序中,所有的网路请求受到严格限制,不满足条件的域名和协议无法请求,具体包括:
域名注册好之后,可以登录微信公众平台配置通信域名了。
2. 云主机和镜像部署
一笔到底的服务器运行代码和配置已经打包成腾讯云 CVM 镜像,大家可以直接使用。
腾讯云用户可以免费领取礼包,体验腾讯云小程序解决方案。
镜像已包含所有小程序的服务器环境与代码,需要体验其它小程序的朋友无需重复部署
3. 配置 HTTPS
镜像中已经部署了 nginx,需要在 /etc/nginx/conf.d
下修改配置中的域名、证书、私钥。
配置完成后,即可启动 nginx。
nginx
4. 域名解析
我们还需要添加域名记录解析到我们的云服务器上,这样才可以使用域名进行 HTTPS 服务。
在腾讯云注册的域名,可以直接使用云解析控制台来添加主机记录,直接选择上面购买的 CVM。
解析生效后,我们在浏览器使用域名就可以进行 HTTPS 访问。
5. 配置云存储 Redis
会话管理依赖 Redis 进行作为缓存管理,开发者可以选择自行搭建 Redis 服务或者直接购买云存储 Redis 服务。
6. 配置云数据库 MongoDB
一笔到底小程序使用 MongoDB 来存储用户绘制的图像路径,要运行小程序开发者需要自行搭建 MongoDB 服务或者直接购买云数据库 MongoDB。
7. 启动一笔到底示例 Node 服务
在镜像的 nginx 配置中(/etc/nginx/conf.d
),已经把/applet/session
的请求转发到 http://127.0.0.1:5757
处理。我们需要把 Node 服务运行起来。Node 代码部署在目录/data/release/qcloud-applet-session
下。
进入该目录:
cd /data/release/qcloud-applet-session
在该目录下有个名为config.js
的配置文件(如下所示),根据注释将appId
、appSecret
、redisConfig
、mongoConfig
修改成自己的配置。
module.exports = {
port: '5757',
ROUTE_BASE_PATH: '/applet',
// 微信小程序 App ID
appId: '',
// 微信小程序 App Secret
appSecret: '',
// Redis 配置
// @see https://www.npmjs.com/package/redis#options-object-properties
redisConfig: {
host: '',
port: '',
password: '',
},
// MongoDB 配置
// @see https://www.qcloud.com/doc/product/240/3979
mongoConfig: {
username: '',
password: '',
host: '',
port: '',
query: '?authMechanism=MONGODB-CR&authSource=admin',
database: 'qcloud-applet-session',
},
};
一笔到底示例使用 pm2 管理 Node 进程,执行以下命令启动 node 服务:
pm2 start process.json
实现
会话层实现
会话层实现包含两个部分:
我们的 Demo 直接使用这两个仓库,可以快速地拥有会话层的能力。
会话层的实现和传统 Cookie 的实现方式类似,都是在 Header 上使用特殊的字段跟踪。一个请求的完整流程如下:
- 客户端(微信小程序)发起请求
request
- weapp-session-client 包装
request
- 首次请求
- 调用
wx.login()
和wx.getUserInfo()
接口获得code
、rawData
和signature
requset
的头部带上code
、rawData
和signature
- 保存
code
供下次调用
- 调用
- 非首次请求
request
的头部带上保存的code
- 首次请求
- 服务器收到请求
request
,中间件从头部提取code
、rawData
和signature
字段- 如果
code
为空,跳到第4
步 - 如果
code
不为空,且rawData
不为空,需要进行签名校验- 使用
code
,appid
、app_secret
请求微信接口获得session_key
和openid
- 如果接口失败,响应
ERR_SESSION_KEY_EXCHANGE_FAILED
- 如果接口失败,响应
- 使用签名算法通过
rawData
和session_key
计算签名signature2
- 对比
signature
和signature2
- 签名一致,解析
rawData
为wxUserInfo
- 把
openid
写入到wxUserInfo
- 把
(code, wxUserInfo)
缓存到 Redis - 把
wxUserInfo
存放在request.$wxUserInfo
里 - 跳到第
4
步
- 把
- 签名不一致,响应
ERR_UNTRUSTED_RAW_DATA
- 签名一致,解析
- 使用
- 如果
code
不为空,但rawData
为空,从 Redis 根据code
查询缓存的用户信息- 找到用户信息,存放在
request.$wxUserInfo
字段里,跳到第4
步 - 没找到用户信息(可能是过期),响应
ERR_SESSION_EXPIRED
- 找到用户信息,存放在
- 如果
request
被业务处理,可以使用request.$wxUserInfo
来获取用户信息(request.$wxUserInfo
可能为空,业务需要自行处理)
源代码
可从 Github 获取 https://github.com/CFETeam/weapp-session