小程序版好友对战实战-wss部署与小程序用户登录时序
上一篇文章是对需求的分析,本次将逐渐进入代码阶段。本次主要的内容包括服务端wss的部署以及小程序端用户授权的时序及逻辑。
wss的配置与部署
微信小程序出于安全考虑,要求所有涉及到网络的操作,必须使用安全的网络请求,如https和wss,却使用的域名必须备案以及不能带端口号。详情请参考微信小程序官方文档
虽然在开发的过程中,微信提供的开发者工具可以忽略https和wss的校验,但程序上线时,就必须使用https和wss协议,所以,在这里,先来介绍下wss协议的部署。
WSS 是 Web Socket Secure 的简称, 它是 WebSocket 的加密版本. 我们知道 WebSocket 中的数据是不加密的, 但是不加密的数据很容易被别有用心的人窃取, 因此为了保护数据安全, 人们将 WebSocket 与 SSL 结合, 实现了安全的 WebSocket 通信, 即 WebSocket Secure.
所以说 WSS 是使用 SSL 进行加密了的 WebSocket 通信技术.
因为我们的对战答题功能对及时性要求比较高,传统的http无法满足要求,所以,我们使用WebSocket作为客户端与服务端,客户端与客户端之间的通讯。
要使用wss首先我们需要购买个域名证书,现在腾讯云和阿里云都有提供免费的域名证书。下面我已腾讯云为例,简单介绍下域名证书的购买。
进入腾讯云官网,在顶部导航中,找到云产品。如下图所示:
点击ssl证书管理。进入证书管理页面,

点击购买证书,进入下级页面,选择免费版

进入下级页面,填写证书基本信息

下一步,验证域名所有权。这里我选择手动DNS验证。点击确认申请。


点击查看证书详情,然后根据腾讯云提供的DNS信息,添加域名解析。

下图是域名解析填写的信息(前提是你要有个域名):

然后,回到腾讯云,点击如下图中的查询按钮:

当出现如下图所示的信息后,稍后几秒后,即可证书即可申请成功。

申请成功后,刷新证书详情页面,如下图,点击下载按钮,将证书下载下来。

将下载后的文件解压备用。
下面进入WebSocket服务端代码实现阶段。这里我使用.net平台开源的Socket框架SuperSocket。首先打开vs,新建一个控制台应用程序项目。
这里我们通过Nuget的方式引用SuperSocket。如下图所示:

分别安装上图中标注的包。安装完成后,然后将之前下载的证书拷贝过来。使用之前解压的文件里的iis文件夹里的证书文件。将iis文件夹中的证书文件拷贝到项目。然后右击证书文件,选择属性,进入属性设置页面。

在复制到输出目录中,选择始终复制。
控制台中的代码:
static void Main(string[] args)
{
#region 证书配置
var certConfig = new CertificateConfig();
certConfig.FilePath = "ttt.vqicard.com.pfx";//证书路径
certConfig.Password = "";//证书密码。申请证书时填写的密码。没填,则此处为空
certConfig.KeyStorageFlags = X509KeyStorageFlags.UserKeySet;
certConfig.ClientCertificateRequired = false;
#endregion
var ws = new WebSocketServer();
var serverConfig = new ServerConfig();
serverConfig.Security = "tls";
serverConfig.Certificate = certConfig;
serverConfig.Ip = IPAddress.Any.ToString();//绑定的ip
serverConfig.Port = ;//监听的端口号。此处填写默认端口。由于我的服务器的443端口已经被占用,
//所以,这里使用其他端口。因为微信不支持带端口的地址,所以,正式部署后,必须设置为443端口。
ws.NewDataReceived += Ws_NewDataReceived;//接收到新数据的回调
ws.NewMessageReceived += Ws_NewMessageReceived;//接收到新字符串的回调
ws.SessionClosed += Ws_SessionClosed;//回话关闭的回调
ws.NewSessionConnected += Ws_NewSessionConnected;//新用户接入的回调
if (ws.Setup(serverConfig))
{
ws.Start();
Console.WriteLine("监听开始");
Console.ReadKey();
}
}
private static void Ws_NewSessionConnected(WebSocketSession session)
{
//接收到新连接后,回复消息给客户端
session.Send("hello");
}
private static void Ws_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason value)
{
}
private static void Ws_NewMessageReceived(WebSocketSession session, string value)
{
}
private static void Ws_NewDataReceived(WebSocketSession session, byte[] value)
{
}
}
代码编写完成后,运行。然后编写小程序端连接WebSocket的代码。
使用wx.connectSocket接口放回一个SocketTask对象。代码如下:
let task = wx.connectSocket({
url: 'wss://ttt.vqicard.com:2018',
success: function (res) {
console.log(res)
}
})
然后SocketTask.onOpen监听连接打开事件。
SocketTask.onClose监听连接关闭事件。
SocketTask.onMessage(CALLBACK)
监听接收到服务器消息的事件。
通过SocketTask.send方法可以向服务器发送数据。
实例代码如下:
task.onOpen(res => {
console.log('连接服务器成功')
})
task.onMessage(res => {
console.log(res)
})
wss的基本配置到这里就完成了。
微信小程序登录时序分析
下图是微信官方提供的小程序的登录逻辑:

从上图我们可以大概分析出用户的小程序端用户授权登录的流程与逻辑。
1.小程序端,调用wx.login()获取code。
2.使用wx.request()将code发送给开发者服务器。
3.开发者服务器使用appid,appsecret,code调用微信提供的接口,获取当前用户的session_key以及openid,这里的session_key是微信服务器生成的针对用户数据加密签名的密钥。
4.开发者服务器使用指定的算法生成足够安全的第三方session。目的是保证session_key的安全性。所以,生成的第三方session应该满足如下条件:长度足够长,避免使用时间戳作为随机参数,设置一定的有效时间,过期即视为不合法。
5.以3rd_session为key,session_key+openid为value,写入session存储。目的是,可以通过3rd_session获取到真实的session_key。
6.将3rd_session返回到小程序端,在小程序端,使用storage存储到本地。
7.后续使用时,先判断3rd_session是否存在,如果不存在则重新从第一步开始。
以上为授权的基本流程,实际操作中,可能会比以上分析的麻烦一点,因为可能会涉及到用户不同意授权。或者以前点过不同意,现在又想点同意的情况。所以,具体的操作,还是通过代码来理解的比较透彻。
代码中,有两个地方是需要给服务器交互的,一个是验证本地存储的session是否合法,另一个是通过code换取第三方session。通常情况下,是使用https的方式与服务器交互,相关的代码在示例中我也写到。但这个答题项目主要是使用wss的方式与服务端通讯,所以,为了方便代码的管理,检测session和换取session的操作我都是用wss的方式,具体的看代码。下面是小程序的代码,注释已经很清楚了,我就不一一解释了。
var wsTask
//app.js
App({
onLaunch: function () {
wsTask = wx.connectSocket({
url: 'ws://192.168.0.253:2018'
})
this.wsTask = wsTask
wsTask.onOpen(()=>{
console.log('连接服务器成功')
})
wsTask.onMessage(msg=>{
var res = JSON.parse(msg.data)
switch(res.option){
case 'checkSession':
if(!res.status){
this.login()
}else{
console.log('登录成功')
}
break
case 'login':
if(res.status){
wx.setStorage({
key: '3rd_session',
data: res.session
})
console.log('登录成功')
}
break
}
})
},
checkSession: function () {
//首先检测登录状态是否失效
wx.checkSession({
complete: cr => {
if (cr.errMsg == 'checkSession:ok') {
//授权状态有效,需判断3rd_session是否存在
let rd_session = wx.getStorageSync('3rd_session')
if (rd_session) {
//第三方session存在
wsTask.send({
data: JSON.stringify({ option:'checkSession',session:rd_session})
})
return
//将第三方session发送到服务器,验证合法性已经是否有效
wx.request({
url: 'checkSessionUrl',
success: function (res) {
if (res.status) {
//根据服务端返回的验证结果进行判断,如果status为1,则表示3rdsession合法,且在有效期内。
} else {
//session无效
this.login()
}
},
fail: function (e) {
console.error(e);//打印错误信息
}
})
} else {
//session不存在,则需重新进入授权流程
this.login()
}
} else {
//授权状态失效,则需重新进入授权流程
this.login()
}
}
})
},
login: function () {
//检查用户是否已同意授权
wx.authorize({
scope: 'scope.userInfo',
complete: res => {
//不允许授权
if (res.errMsg != 'authorize:ok') {
//则获取用户的授权设置
wx.getSetting({
success: r => {
//未开启授权
if (!r.authSetting['scope.userInfo']) {
//询问是否开启授权
wx.showModal({
title: '登录',
content: '小程序需要使用您的授权信息,是否继续?',
success: res => {
console.log(res)
if (res.confirm) {
//同意开启授权,则跳转到设置页面,由用户打开授权。用户打开授权后,由用户操作,返回小程序,此时可以再onShow方法中再次调用login方法。
wx.openSetting()
}
}
})
}
}
})
} else {
//表示已授权,此时,可以调用登录接口
wx.login({
success: res => {
if(res.errMsg=='login:ok'){
wsTask.send({data:JSON.stringify({option:'login',code:res.code})})
return
wx.request({
url: 'loginUrl',
data:{code:res.code},
success:rq=>{
//将此处返回的3rdsession保存在storage中,整个授权流程结束
}
})
}
}
})
}
}
})
},
onShow: function () {
this.checkSession()
},
globalData: {
userInfo: null
}
})
服务端的代码如下:
private static void Ws_NewMessageReceived(WebSocketSession session, string value)
{
var jobj = JsonConvert.DeserializeObject<JObject>(value);
var option = jobj.Value<string>("option");
switch (option)
{
case "checkSession":
var rdsession = jobj.Value<string>("session");
var model = wxUserlist.FirstOrDefault(f => f.MyKey == rdsession);
session.Send(JsonConvert.SerializeObject(new { option = option, status =model!=null?:}));
break;
case "login":
var code = jobj.Value<string>("code");
var res = LoginApi.CodeToMySession("你的appid", "你的appsecret", code);
wxUserlist.Add(res);
session.Send(JsonConvert.SerializeObject(new { status=,session=res.MyKey,option=option}));
break;
}
}
如需源码,请扫描二维码,关注微信公众号。回复:对战二
小程序版好友对战实战-wss部署与小程序用户登录时序的更多相关文章
- 实战3--应用EL表达式判断用户登录信息
1.判断用户名是否为空,空则显示提示信息 (1)编写index.jsp页面 <%@ page language="java" contentType="text/h ...
- mxonline实战3,编写首页及用户登录页面1
对应github地址:首页和用户登陆1 一. 显示首页 1. 修改mxonline/setttings.py 在TEMPLATES代码块修改DIRS为 'DIRS': [os. ...
- 如果您想确保Windows 10在新用户登录时不安装内置应用程序,则必须删除所有配置的应用程序。
原文 如果您想确保Windows 10在新用户登录时不安装内置应用程序,则必须删除所有配置的应用程序. 本文的内容 已安装与配置的应用程序 删除配置的应用程序 安装与配置的应用程序^ 在介绍如何删除所 ...
- 微信小程序版2048
最近流行微信"跳一跳"小游戏,我也心血来潮写了一个微信小程序版2048,本篇文章主要分享实现2048的算法以及注意的点,一起来学习吧!(源码地址见文章末尾) 算法 1.生成4* ...
- 七天开发进度(七)(微信小程序版(二)记账本)
终于把小程序版弄完了,不过这并不能称之为是我的作品,因为我还没有彻底学会小程序,对JavaScript语言还有很多不会的地方,没有掌握, 这次的程序是通过学习网上的多个教程,多个案例结合拼凑模仿者人家 ...
- 某客的《微信小程序》从基础到实战视频教程
第 1 部分 微信小程序从基础到实战课程概要 第 1 节 微信小程序从基础到实战课程概要 1.1微信小程序从基础到实战课程概要 第 2 部分 初识微信小程序 第 1 节 微信小程序简 ...
- 《黑客攻防技术宝典Web实战篇@第2版》读书笔记1:了解Web应用程序
读书笔记第一部分对应原书的第一章,主要介绍了Web应用程序的发展,功能,安全状况. Web应用程序的发展历程 早期的万维网仅由Web站点构成,只是包含静态文档的信息库,随后人们发明了Web浏览器用来检 ...
- 微信小程序版博客——开发汇总总结(附源码)
花了点时间陆陆续续,拼拼凑凑将我的小程序版博客搭建完了,这里做个简单的分享和总结. 整体效果 对于博客来说功能页面不是很多,且有些限制于后端服务(基于ghost博客提供的服务),相关样式可以参考截图或 ...
- 微信小程序基础之在微信上显示和体验小程序?
随着小程序正式上线,用户现在可以通过二维码.搜索等方式体验到开发者们开发的小程序了. 用户只要将微信更新至最新版本,体验过小程序后,便可在发现页面看到小程序TAB,但微信并不会通过这个地方向用户推荐小 ...
随机推荐
- BZOJ 1426: 收集邮票 [DP 期望 平方]
传送门 题意: 有n种不同的邮票,皮皮想收集所有种类的邮票.唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n.但是由于凡凡也很喜欢邮 ...
- BZOJ 2738: 矩阵乘法 [整体二分]
给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 愚蠢的名字...... 整体二分,影响因子就是矩阵里的数 把$\le mid$的矩阵元素加到二维树状数组里然后询问分成两组就行 ...
- 监督学习:随机梯度下降算法(sgd)和批梯度下降算法(bgd)
线性回归 首先要明白什么是回归.回归的目的是通过几个已知数据来预测另一个数值型数据的目标值. 假设特征和结果满足线性关系,即满足一个计算公式h(x),这个公式的自变量就是已知的数据x,函数值h(x)就 ...
- [HAOI2009]毛毛虫
题目描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入输出格 ...
- 用es6的Array.reduce()方法计算一个字符串中每个字符出现的次数
有一道经典的字符串处理的问题,统计一个字符串中每个字符出现的次数. 用es6的Array.reduce()函数配合“...”扩展符号可以更方便的处理该问题. s='abananbaacnncn' [. ...
- linux下ACE的编译与安装
1.环境变量的设置vim /etc/profile 2.然后输入export ACE_ROOT=/root/ACE/ACE_wrappers export MPC_ROOT=$ACE_ROOT/MPC ...
- C++ Primer Plus阅读笔记
cin进行文本输入 使用cin获取字符的基本方式 char ch; cin >> ch; cin读取char值时将忽略空格和换行符.此外,发送给cin的输入被缓冲.这意味着只有在用户按下回 ...
- MySQL select语句直接导出数据
select * into outfile '文件存放路径' from 表名; (先记下来,还未测试)
- How to install tcpping on Linux.md
To install tcptraceroute on Debian/Ubuntu: $ sudo apt-get install tcptraceroute To install tcptracer ...
- 对html语义化的理解
所有人都知道html即超文本标记语言或超文本链接标示语言,是目前网络上应用最为广泛的语言,也是构成网页文档的主要语言. html标签中的大部分都是由"语义化"标签所担任 那么,它有 ...