小程序版好友對戰實戰-wss部署與小程序用戶登陸時序


上一篇文章是對需求的分析,本次將逐漸進入代碼階段。本次主要的內容包括服務端wss的部署以及小程序端用戶受權的時序及邏輯。html

 


wss的配置與部署算法

微信小程序出於安全考慮,要求全部涉及到網絡的操做,必須使用安全的網絡請求,如https和wss,卻使用的域名必須備案以及不能帶端口號。詳情請參考微信小程序官方文檔小程序

雖然在開發的過程當中,微信提供的開發者工具能夠忽略https和wss的校驗,但程序上線時,就必須使用https和wss協議,因此,在這裏,先來介紹下wss協議的部署。微信小程序

WSS 是 Web Socket Secure 的簡稱, 它是 WebSocket 的加密版本. 咱們知道 WebSocket 中的數據是不加密的, 可是不加密的數據很容易被別有用心的人竊取, 所以爲了保護數據安全, 人們將 WebSocket 與 SSL 結合, 實現了安全的 WebSocket 通訊, 即 WebSocket Secure.api

因此說 WSS 是使用 SSL 進行加密了的 WebSocket 通訊技術.安全

由於咱們的對戰答題功能對及時性要求比較高,傳統的http沒法知足要求,因此,咱們使用WebSocket做爲客戶端與服務端,客戶端與客戶端之間的通信。服務器

要使用wss首先咱們須要購買個域名證書,如今騰訊雲和阿里雲都有提供免費的域名證書。下面我已騰訊云爲例,簡單介紹下域名證書的購買。微信

進入騰訊雲官網,在頂部導航中,找到雲產品。以下圖所示:網絡

點擊ssl證書管理。進入證書管理頁面,session

 

點擊購買證書,進入下級頁面,選擇免費版

 

進入下級頁面,填寫證書基本信息

 

下一步,驗證域名全部權。這裏我選擇手動DNS驗證。點擊確認申請。

 

 

點擊查看證書詳情,而後根據騰訊雲提供的DNS信息,添加域名解析。

 

下圖是域名解析填寫的信息(前提是你要有個域名):

 

而後,回到騰訊雲,點擊以下圖中的查詢按鈕:

 

當出現以下圖所示的信息後,稍後幾秒後,便可證書便可申請成功。

 

申請成功後,刷新證書詳情頁面,以下圖,點擊下載按鈕,將證書下載下來。

 

將下載後的文件解壓備用。

下面進入WebSocket服務端代碼實現階段。這裏我使用.net平臺開源的Socket框架SuperSocket。首先打開vs,新建一個控制檯應用程序項目。

這裏咱們經過Nuget的方式引用SuperSocket。以下圖所示:

 

分別安裝上圖中標註的包。安裝完成後,而後將以前下載的證書拷貝過來。使用以前解壓的文件裏的iis文件夾裏的證書文件。將iis文件夾中的證書文件拷貝到項目。而後右擊證書文件,選擇屬性,進入屬性設置頁面。

 

在複製到輸出目錄中,選擇始終複製。

控制檯中的代碼:

   

 1 static void Main(string[] args)
 2 
 3         {
 4 
 5             #region 證書配置
 6 
 7             var certConfig = new CertificateConfig();
 8 
 9             certConfig.FilePath = "ttt.vqicard.com.pfx";//證書路徑
10 
11             certConfig.Password = "123123";//證書密碼。申請證書時填寫的密碼。沒填,則此處爲空
12 
13             certConfig.KeyStorageFlags = X509KeyStorageFlags.UserKeySet;
14 
15             certConfig.ClientCertificateRequired = false;
16 
17             #endregion
18 
19             var ws = new WebSocketServer();
20 
21             var serverConfig = new ServerConfig();
22 
23             serverConfig.Security = "tls";
24 
25             serverConfig.Certificate = certConfig;
26 
27             serverConfig.Ip = IPAddress.Any.ToString();//綁定的ip
28 
29             serverConfig.Port = 2018;//監聽的端口號。此處填寫默認端口。因爲個人服務器的443端口已經被佔用,
30 
31             //因此,這裏使用其餘端口。由於微信不支持帶端口的地址,因此,正式部署後,必須設置爲443端口。
32 
33             ws.NewDataReceived += Ws_NewDataReceived;//接收到新數據的回調
34 
35             ws.NewMessageReceived += Ws_NewMessageReceived;//接收到新字符串的回調
36 
37             ws.SessionClosed += Ws_SessionClosed;//回話關閉的回調
38 
39             ws.NewSessionConnected += Ws_NewSessionConnected;//新用戶接入的回調
40 
41             if (ws.Setup(serverConfig))
42 
43             {
44 
45                 ws.Start();
46 
47                 Console.WriteLine("監聽開始");
48 
49                 Console.ReadKey();
50 
51             }
52 
53         }
54 
55         private static void Ws_NewSessionConnected(WebSocketSession session)
56 
57         {
58 
59             //接收到新鏈接後,回覆消息給客戶端
60 
61             session.Send("hello");
62 
63         }
64 
65         private static void Ws_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason value)
66 
67         {
68 
69         }
70 
71         private static void Ws_NewMessageReceived(WebSocketSession session, string value)
72 
73         {
74 
75         }
76 
77         private static void Ws_NewDataReceived(WebSocketSession session, byte[] value)
78 
79         {
80 
81         }
82 
83     }

 

 



代碼編寫完成後,運行。而後編寫小程序端鏈接WebSocket的代碼。

使用wx.connectSocket接口放回一個SocketTask對象。代碼以下:

 1 let task = wx.connectSocket({
 2 
 3 url: 'wss://ttt.vqicard.com:2018',
 4 
 5 success: function (res) {
 6 
 7 console.log(res)
 8 
 9 }
10 
11 })

 

而後SocketTask.onOpen監聽鏈接打開事件。

SocketTask.onClose監聽鏈接關閉事件。

SocketTask.onMessage(CALLBACK)

監聽接收到服務器消息的事件。

經過SocketTask.send方法能夠向服務器發送數據。

實例代碼以下:

  

 1 task.onOpen(res => {
 2 
 3 console.log('鏈接服務器成功')
 4 
 5 })
 6 
 7 task.onMessage(res => {
 8 
 9 console.log(res)
10 
11 })

 

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的方式,具體的看代碼。下面是小程序的代碼,註釋已經很清楚了,我就不一一解釋了。

  1 var wsTask
  2 
  3 //app.js
  4 
  5 App({
  6 
  7 onLaunch: function () {
  8 
  9 wsTask = wx.connectSocket({
 10 
 11  url: 'ws://192.168.0.253:2018'
 12 
 13 })
 14 
 15 this.wsTask = wsTask
 16 
 17 wsTask.onOpen(()=>{
 18 
 19  console.log('鏈接服務器成功')
 20 
 21 })
 22 
 23 wsTask.onMessage(msg=>{
 24 
 25  var res = JSON.parse(msg.data)
 26 
 27  switch(res.option){
 28 
 29  case 'checkSession':
 30 
 31  if(!res.status){
 32 
 33   this.login()
 34 
 35  }else{
 36 
 37   console.log('登陸成功')
 38 
 39  }
 40 
 41  break
 42 
 43  case 'login':
 44 
 45   if(res.status){
 46 
 47   wx.setStorage({
 48 
 49    key: '3rd_session',
 50 
 51    data: res.session
 52 
 53   })
 54 
 55   console.log('登陸成功')
 56 
 57   }
 58 
 59  break
 60 
 61  }
 62 
 63 })
 64 
 65 },
 66 
 67 checkSession: function () {
 68 
 69 //首先檢測登陸狀態是否失效
 70 
 71 wx.checkSession({
 72 
 73  complete: cr => {
 74 
 75  if (cr.errMsg == 'checkSession:ok') {
 76 
 77   //受權狀態有效,需判斷3rd_session是否存在
 78 
 79   let rd_session = wx.getStorageSync('3rd_session')
 80 
 81   if (rd_session) {
 82 
 83   //第三方session存在
 84 
 85   wsTask.send({
 86 
 87    data: JSON.stringify({ option:'checkSession',session:rd_session})
 88 
 89   })
 90 
 91   return
 92 
 93   //將第三方session發送到服務器,驗證合法性已是否有效
 94 
 95   wx.request({
 96 
 97    url: 'checkSessionUrl',
 98 
 99    success: function (res) {
100 
101    if (res.status) {
102 
103     //根據服務端返回的驗證結果進行判斷,若是status爲1,則表示3rdsession合法,且在有效期內。
104 
105    } else {
106 
107     //session無效
108 
109     this.login()
110 
111    }
112 
113    },
114 
115    fail: function (e) {
116 
117    console.error(e);//打印錯誤信息
118 
119    }
120 
121   })
122 
123   } else {
124 
125   //session不存在,則需從新進入受權流程
126 
127   this.login()
128 
129   }
130 
131  } else {
132 
133   //受權狀態失效,則需從新進入受權流程
134 
135   this.login()
136 
137  }
138 
139  }
140 
141 })
142 
143 },
144 
145 login: function () {
146 
147 //檢查用戶是否已贊成受權
148 
149 wx.authorize({
150 
151  scope: 'scope.userInfo',
152 
153  complete: res => {
154 
155  //不容許受權
156 
157  if (res.errMsg != 'authorize:ok') {
158 
159   //則獲取用戶的受權設置
160 
161   wx.getSetting({
162 
163   success: r => {
164 
165    //未開啓受權
166 
167    if (!r.authSetting['scope.userInfo']) {
168 
169    //詢問是否開啓受權
170 
171    wx.showModal({
172 
173     title: '登陸',
174 
175     content: '小程序須要使用您的受權信息,是否繼續?',
176 
177     success: res => {
178 
179     console.log(res)
180 
181     if (res.confirm) {
182 
183      //贊成開啓受權,則跳轉到設置頁面,由用戶打開受權。用戶打開受權後,由用戶操做,返回小程序,此時能夠再onShow方法中再次調用login方法。
184 
185      wx.openSetting()
186 
187     }
188 
189     }
190 
191    })
192 
193    }
194 
195   }
196 
197   })
198 
199  } else {
200 
201   //表示已受權,此時,能夠調用登陸接口
202 
203   wx.login({
204 
205   success: res => {
206 
207    if(res.errMsg=='login:ok'){
208 
209    wsTask.send({data:JSON.stringify({option:'login',code:res.code})})
210 
211    return
212 
213    wx.request({
214 
215     url: 'loginUrl',
216 
217     data:{code:res.code},
218 
219     success:rq=>{
220 
221     //將此處返回的3rdsession保存在storage中,整個受權流程結束
222 
223     }
224 
225    })
226 
227    }
228 
229   }
230 
231   })
232 
233  }
234 
235  }
236 
237 })
238 
239 },
240 
241 onShow: function () {
242 
243 this.checkSession()
244 
245 },
246 
247 globalData: {
248 
249 userInfo: null
250 
251 }
252 
253 })

 

服務端的代碼以下:

 1 private static void Ws_NewMessageReceived(WebSocketSession session, string value)
 2 
 3 {
 4 
 5 var jobj = JsonConvert.DeserializeObject<JObject>(value);
 6 
 7 var option = jobj.Value<string>("option");
 8 
 9 switch (option)
10 
11 {
12 
13 case "checkSession":
14 
15 var rdsession = jobj.Value<string>("session");
16 
17 var model = wxUserlist.FirstOrDefault(f => f.MyKey == rdsession);
18 
19 session.Send(JsonConvert.SerializeObject(new { option = option, status =model!=null?1:0}));
20 
21 break;
22 
23 case "login":
24 
25 var code = jobj.Value<string>("code");
26 
27 var res = LoginApi.CodeToMySession("你的appid", "你的appsecret", code);
28 
29 wxUserlist.Add(res);
30 
31 session.Send(JsonConvert.SerializeObject(new { status=1,session=res.MyKey,option=option}));
32 
33 break;
34 
35 }
36 
37 }
38 
39  

 

如需源碼,請掃描二維碼,關注微信公衆號。回覆:對戰二

 

相關文章
相關標籤/搜索