中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》
  • 首頁 > 
  • 教程 > 
  • 開發技術 > 
  • 微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

發布時間:2021-07-09 09:13:35 來源:億速云 閱讀:371 作者:小新 欄目:開發技術

小編給大家分享一下微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

1 概要說明

使用微信掃描登錄相信大家都不會陌生吧,二維碼與手機結合產生了不同應用場景,基于二維碼的應用更是比較廣泛。為了滿足ios、android客戶端與web短信平臺的結合,特開發了基于SinglarR消息推送機制的掃描登錄。本系統涉及到以下知識點:

SignalR:http://signalr.net/ 這官網,ASP.NET SignalR 是為 ASP.NET 開發人員提供的一個庫,可以簡化開發人員將實時 Web 功能添加到應用程序的過程。實時 Web 功能是指這樣一種功能:當所連接的客戶端變得可用時服務器代碼可以立即向其推送內容,而不是讓服務器等待客戶端請求新的數據。

二維碼:使用的QRCode類庫,https://github.com/jeromeetienne/jquery-qrcode

MVC5:開發環境是基于MVC5

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

2、系統關系圖

在實現本功能前,有點不是太確定能否拿下。

所謂萬事開頭難,通過查詢想資料及自己歸納分析:系統涉及到手機客戶端、瀏覽者、服務端,實現掃描登錄也就是三者之間是如何協調工作的。通過axure畫出如下關系圖:

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

移動客戶端、瀏覽者、服務端三者協作關系圖

【M】:表示移動端 【B】:表示瀏覽者(瀏覽器客戶端) 【S】:服務端,消息推送者及掃描認證接口發布者

步驟說明:

Step(步驟)1 ,【B】瀏覽登錄頁面,Step2【S】產生一個標識符UUID,并推送給B,生成登錄二維碼;

Step3,【M】掃描二維碼,前提條件是【M】已登錄,Step4【M】解析二維碼信息獲取UUID;

Step5,【M】向服務端發送UUID+登錄信息,Step6【S】對UUID+登錄信息進行相關解析認證,Step6 UUID認證,不通過認證,則到Step6-1 重新生成UUID循環Step 2與并Step6-2 返回給【M】UUID認證失敗原因,Step6 通過認證,Step6-2轉到登錄信息認證,Step 7登錄信息認證,失敗Step7-3重新生成UUID循環Step 2,成功則Step7-1推送給【B】跳轉到首頁。

3、SignalR循環消息推送

3.1 引用SignalR

由于本人用的是VS15Preview4,可以直接使用Nuget可視化管理工具進行安裝:Tools—>Nuget Package Manager—>Manage Nuget Packages for Solution…,打開以下界面:

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

在Browser 標簽下輸入SignalR,查詢到Microsoft.AspNet.SignalR

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄/p>

找到對應的項目,點擊“Install”安裝按鈕即可引用相關類庫,同時應用下載相關js庫。

關于SignalR的知識點,可以到官網 http://www.asp.net/signalr 進行深入學習。

3.2 服務端SignalR實現

服務端要向客戶端推送UUID,對于UUID唯一標識符,具有重要特性:(1)有時間限制,120秒之內掃碼有效;(2)具有一定的狀態。對應的聲明周期就是:生成—>推送—>狀態判斷—>手機端掃描—>驗證UUID—>狀態判斷—>銷毀等系列過程。

服務端的核心代碼將單獨建立一個項目去實現:

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

3.2.1 Nofifier.cs通知類

本類將連接QRCodeHub與SessionTimer

using Microsoft.AspNet.SignalR;namespace TxSms.SingalR
{
public static class Notifier 
{ 
private static readonly IHubContext Context = GlobalHost.ConnectionManager.GetHubContext<QRCodeHub>(); 
public static void SessionTimeOut(string connectionId, int time) 
{ 
Context.Clients.Client(connectionId).alertClient(time); 
} 
public static void SendElapsedTime(string connectionId, int time) 
{
Context.Clients.Client(connectionId).sendElapsedTime(time); 
} 
public static void SendQRCodeUUID(string connectionId, string uuid)
{ Context.Clients.Client(connectionId).sendQRCodeUUID(uuid);
} 
}
}

3.2.2 QRCodeHub.cs SignalR核心實現

SignalR的核心代碼:

using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;
namespace TxSms.SingalR
{ 
/// <summary> 
/// 二維碼推送
/// 
</summary> 
//[HubName("qrcode")] public class QRCodeHub : Hub {
/// <summary> /// 給客戶端發送時間間隔 /// 
</summary> /// <param name="time">
</param>
public void SendTimeOutNotice(int time) 
{
Clients.Client(Context.ConnectionId).alertClient(time);
}
public void CheckElapsedTime(int time) 
{
Clients.Client(Context.ConnectionId).sendElapsedTime(time); } 
/// <summary> 
/// 發送二維碼UUID內容
///
</summary>
/// <param name="uuid">
</param> 
public void SendQRCodeUUID(string uuid) 
{ 
Clients.Client(Context.ConnectionId).sendQRCodeUUID(uuid); }
///
<summary>
/// Called when the connection connects to this hub instance.
/// </summary> ///
<returns>A <see cref="T:System.Threading.Tasks.Task" />
</returns> public override Task OnConnected() 
{
SessionTimer.StartTimer(Context.ConnectionId); return base.OnConnected();
}
/// <summary> 
/// Called when a connection disconnects from this hub gracefully or due to a timeout. 
///
</summary> 
/// 
<param name="stopCalled">
/// true, if stop was called on the client closing the connection gracefully;
/// false, if the connection has been lost for longer than the /// <see cref="P:Microsoft.AspNet.SignalR.Configuration.IConfigurationManager.DisconnectTimeout" />. 
/// Timeouts can be caused by clients reconnecting to another SignalR server in scaleout. /// </param> /// <returns>A <see cref="T:System.Threading.Tasks.Task" />
</returns> public override Task OnDisconnected(bool stopCalled) { SessionTimer.StopTimer(Context.ConnectionId); return base.OnDisconnected(stopCalled);
} 
/// <summary> /// 
Called when the connection reconnects to this hub instance. /// </summary> ///
<returns>A <see cref="T:System.Threading.Tasks.Task" />
</returns> public override Task OnReconnected()
{
if (!SessionTimer.Timers.ContainsKey(Context.ConnectionId)) 
{ 
SessionTimer.StartTimer(Context.ConnectionId);
} 
return base.OnReconnected(); }
///
<summary> /// 重置時鐘 /// 
</summary> public void ResetTimer() 
{ SessionTimer timer;
if (SessionTimer.Timers.TryGetValue(Context.ConnectionId, out timer))
{ timer.ResetTimer(); 
} 
else
{
SessionTimer.StartTimer(Context.ConnectionId);
}
} /// 
<summary> /// 
發送普通消息 /// 
</summary> ///
<param name="name">
</param> /// <param name="message">
</param>
public void Send(string name, string message) 
{ 
Clients.All.addNewMessageToPage(name, message); 
} 
}
}

3.2.3 SessionTimer.cs 對應客戶端時鐘

對【B】來說,產生一個獨立的timer,進行按1s間隔發送消息。

using System;using System.Collections.Concurrent;using System.Timers;namespace TxSms.SingalR{ public class SessionTimer : IDisposable 
{ 
///
<summary> /// 存儲客戶端對應的Timer /// 
</summary> 
public static readonly ConcurrentDictionary<string, SessionTimer> Timers; private readonly Timer _timer; static SessionTimer() 
{ Timers = new ConcurrentDictionary<string, SessionTimer>(); 
}
/// <summary> /// 構造函數 /// </summary> /// 
<param name="connectionId"></param> private SessionTimer(string connectionId)
{
ConnectionId = connectionId; _timer = new Timer { Interval = Utility.ActivityTimerInterval() }; _timer.Elapsed += (s, e) => MonitorElapsedTime(); _timer.Start(); 
}
public int TimeCount { get; set; } /// <summary> /// 客戶端連接Id ///
</summary> public string ConnectionId { get; set; } /// <summary> /// 啟動Timer /// 
</summary> ///
<param name="connectionId">
</param>
public static void StartTimer(string connectionId) 
{ 
var newTimer = new SessionTimer(connectionId);
if (!Timers.TryAdd(connectionId, newTimer)) 
{ newTimer.Dispose();
}
} 
/// <summary>
/// 停止Timer /// </summary> ///
<param name="connectionId">
</param> public static void StopTimer(string connectionId) 
{
SessionTimer oldTimer; 
if (Timers.TryRemove(connectionId, out oldTimer))
{ 
oldTimer.Dispose();
} 
} 
/// <summary> /// 
重置Timer /// 
</summary> public void ResetTimer() { TimeCount = 0; _timer.Stop();
_timer.Start(); 
} 
public void Dispose()
{ 
// Stop might not be necessary since we call Dispose _timer.Stop(); _timer.Dispose(); 
} ///

<summary> ///
給客戶端發送消息 /// 
</summary> private void MonitorElapsedTime() 
{ Utility.ClearExpiredUUID(); var uuid = Utility.GetUUID(ConnectionId); 
//if (TimeCount >= Utility.TimerValue()) 
//{ // StopTimer(ConnectionId);
// Notifier.SendQRCodeUUID(ConnectionId, uuid); // Notifier.SessionTimeOut(ConnectionId, TimeCount); 
//} 
//else //
{ Notifier.SendQRCodeUUID(ConnectionId, uuid);
Notifier.SendElapsedTime(ConnectionId, TimeCount); 
//}
TimeCount++;
if (TimeCount > 1000) 
{ 
TimeCount = 0;
} } }}

3.2.4 Utility.cs 基礎配置

滿足時鐘、獲取QRCode等

using TxSms.Actions;namespace TxSms.SingalR
{
internal class Utility { public static int IntNum = 0; /// 
<summary> /// 時間間隔 /// 
</summary> ///
<returns></returns> 
public static int TimerValue() 
{ return 1000;
} 
public static double ActivityTimerInterval()
{ return 1000.0;
}
/// <summary> /// 獲取當前UUID
/// </summary>
/// <returns></returns> public static string GetUUID(string connectionId)
{
try 
{ 
var model = new QRCodeAction().GetValidModel(connectionId); 
return model.ToJson(connectionId); 
} 
catch 
{ return "ERROR"; 
} } /// <summary> /// 刪除過期UUID /// 
</summary> public static void ClearExpiredUUID() { IntNum++; 
if (IntNum <= 1000) return; 
new QRCodeAction().ClearExpiredUUID();
IntNum = 0; } }}

3.2.5 SignalR在MVC中啟動配置

在MVC中,啟動項目進行如下配置:

using Microsoft.Owin;using Owin;
[assembly: OwinStartup(typeof(TxSms.Web.Startup))]namespace TxSms.Web{ public partial class Startup 
{ 
public void Configuration(IAppBuilder app) 
{ //啟動SignalR app.MapSignalR(); ConfigureAuth(app); 
} }}

3.2.6 其他類庫說明

QRCodeAction.cs:維護UUID,創建、保存、狀態更改、刪除等。

QRModel.cs:UUID實體

所有文件,可在《7、總結與下載》中下載。

3.3 客戶端SignalR實現

添加SignalR js庫:

<script type="text/javascript" src="~/Scripts/jquery.signalR-2.2.1.min.js">
</script> 
<script type="text/javascript" src="~/signalr/hubs">
</script

兩者必須都引用。

調用接口如下:

 var codeUUID = "";
 $(function () {
 // Reference the auto-generated proxy for the hub. var qrcode = $.connection.qRCodeHub;
 // Create a function that the hub can call back to display messages. qrcode.client.addNewMessageToPage = function (name, message)
 { // Add the message to the page. console.log(message); //jQuery('#divQRCode').qrcode({ width: 180, height: 180, correctLevel: 0, text: message }); 
 }; 
 qrcode.client.sendElapsedTime = function (time) { console.log(time); 
 }; 
 qrcode.client.sendQRCodeUUID = function (uuid) { console.log("sendQRCodeUUID");
 console.log(codeUUID); if (codeUUID === uuid) { return; } codeUUID = uuid; if (codeUUID !== "ERROR") { var jsonUUID = $.parseJSON(codeUUID);
 if (jsonUUID.islogin === 1) { //判斷是否登錄 window.location.href = "/Home/Index/@Model.Name"; } } $("#divQRCode").html("");
 $('#divQRCode').qrcode({ width: 180, height: 180, correctLevel: 0, text: codeUUID });
 };
 // Start the connection. $.connection.hub.start().done(function () { //qrcode.server.updateConnectionId($.connection.hub.id); qrcode.server.send("qrcode", Math.random());
 });
 });

以上代碼包括相關二維碼的生成。

4、二維碼的生成與存儲數據解析

4.1 二維碼的生成

二維碼類庫選擇https://github.com/jeromeetienne/jquery-qrcode 一個QRCode原生態js類庫,jquery對其進行了擴展。

添加script標簽:

<script type="text/javascript" src="~/Scripts/qrcode.min.js">
</script> 
<script type="text/javascript" src="~/Scripts/jquery.qrcode.min.js">
</script>

定義div標簽,用來呈現二維碼:

 <!--二維碼登錄開始--> 
<div> 
<div>安全登錄 防止被盜</div>
 <div>
</div> 
<div>掃一掃登錄</div> 
</div> 
<!--二維碼登錄結束-->

呈現二維碼:

$("#divQRCode").html(""); 
$('#divQRCode').qrcode({ width: 180, height: 180, correctLevel: 0, text: codeUUID });

通過3與4,可實現具有180秒生命周期二維碼的生成,對于不同的瀏覽者,生成的二維碼是不同的,效果如下:

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

4.2 二維碼存儲的是什么

二維碼生成了,但是存儲的是什么呢?首先我們看下以下的二維:

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

hbuilder官網

千牛電腦客戶端二維碼登錄界面

顯然,掃描這兩個圖片上的二維碼會得到不同的結果。對某些二維碼的解碼要對應配套的客戶端才能起到作用,否則用其他工具解析出來也就是字符串。

在本系統中,二維碼存儲的是一個json對象,格式為:

{"connectionid":"19c12e95-26d7-410c-8292-2a3afdd1a4da","uuid":"a04702df-6a52-4e1c-be8b-9b3dbeef4d72","islogin":0,"isvalid":1}

connectionid:客戶端與SignalR聯系的id,其格式為Guid

uuid:對應connectionid產生的一個唯一標識符,其格式為Guidislogin:當前connectionid連接是否已登錄,1—>表示登錄,0—>未登錄isvalid:當前connectionid對應的uuid是否有效,1—>表示有效,0—>表示失效

手機客戶端掃描之后,可根據這些參數情況進行判斷,是否向服務端發送請求。在做掃描應用(比如掃描登錄)時,要依據業務場景進行消息傳遞,生成對應二維碼,并不局限于json對象、url地址等。

總結下來,二維碼應用場景,如下圖:

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

5、掃描認證接口

為了滿足【M】端掃描之后,提交UUID+用戶信息進行認證,建立QRCode API接口。接口任務比較簡單,就是對UUID合法性進行判斷,然后判斷用戶信息登錄情況,更改UUID的登錄狀態。

5.1 輸入參數

using Abp.Application.Services.Dto;using System;using System.ComponentModel.DataAnnotations;
namespace TxSms.Inputs{ /// <summary> /// 二維碼登錄認證 /// </summary> [Serializable]
public class QRCodeVerifyInput : IInputDto { /// <summary> /// 構造函數 /// </summary>
public QRCodeVerifyInput() { ConnectionId = Guid.Empty.ToString();
UUID = Guid.Empty; UserName = Password = ""; } /// <summary> /// 當前回話ID /// </summary> 
[DisplayFormat(ConvertEmptyStringToNull = false)] public string ConnectionId { get; set;
} /// <summary> /// 唯一標識符號 /// </summary> 
public Guid UUID { get; set; } /// <summary> /// 用戶賬號 /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string UserName { get; set; } /// <summary> /// 登錄密碼 /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)] 
public string Password { get; set; } /// <summary> /// 平臺 /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)] 
public string Platform { get; set; 
} }}

5.2 輸出參數

using Abp.Application.Services.Dto;
using Newtonsoft.Json;using System.ComponentModel.DataAnnotations;using System.Web.Mvc;using TxSms.MVC;namespace TxSms.Outputs{ /// <summary> /// 輸出基類 /// </summary> [ModelBinder(typeof(EmptyStringModelBinder))] public class TxSmsOutputDto : IOutputDto { /// <summary> /// 構造函數 /// 
</summary> public TxSmsOutputDto() 
{ Result = 0; //默認為0,表示初始值或正確 Message = ""; 
} /// <summary> /// 錯誤代碼 ///
</summary> [JsonProperty("Result")] public int Result { get; set; } /// <summary> /// 錯誤信息 /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)] [JsonProperty("Message")] 
public string Message { get; set; } }}

5.3 API接口

using System;using System.Threading.Tasks;using System.Web.Http;using TxSms.Actions;using TxSms.Inputs;using TxSms.Outputs;namespace TxSms{ /// <summary> /// 二維碼接口 /// </summary> public class QRCodeController : TxSmsApiController { /// <summary> /// 二維碼登錄認證 /// </summary> /// <returns> /// 0:登錄成功;-1:參數錯誤 -2:ConnectionId、UUID、UserName、Password不允許為空-3:ConnectionId回話id不存在-4:UUID輸入錯誤-5:UUID已過期 /// -6:本UUID已登錄-7:登錄賬號已停用-8:登錄賬號已刪除-9:登錄密碼輸入錯誤-10:登錄賬號不存在 /// </returns> [AllowAnonymous] [HttpPost] public async Task<TxSmsOutputDto> QRCodeVerify([FromBody]QRCodeVerifyInput model) { TxSmsOutputDto result = new TxSmsOutputDto(); #region 參數驗證 if (model.IsNull()) { result.Result = -1; result.Message = "參數錯誤"; return result; } if (model.ConnectionId.IsNullOrEmpty() || model.UUID.Equals(Guid.Empty) || model.UserName.IsNullOrEmpty() || model.Password.IsNullOrEmpty()) { result.Result = -2; result.Message = "ConnectionId、UUID、UserName、Password不允許為空"; return result; } #endregion 參數驗證 #region 有效性判斷 //驗證ConnectionId合法性 if (QRCodeAction.QRCodeLists.ContainsKey(model.ConnectionId)) { result.Result = -3; result.Message = "ConnectionId回話id不存在"; return result; } //驗證UUID有效性 var findCode = QRCodeAction.QRCodeLists[model.ConnectionId]; if (!model.UUID.Equals(findCode.UUID)) { result.Result = -4; result.Message = "UUID輸入錯誤"; return result; } if (!findCode.IsValid()) { result.Result = -5; result.Message = "UUID已過期"; return result; } if (findCode.IsLogin) { result.Result = -6; result.Message = "本UUID已登錄"; return result; } #endregion 有效性判斷 LoginUserNameInput loginParam = new LoginUserNameInput { UserName = model.UserName, Password = model.Password, Platform = model.Platform }; LoginOutput loginResult = await new SessionController().LoginUserName(loginParam); switch (loginResult.Result) { case -1: result.Result = -7; result.Message = "登錄賬號已停用"; break; case -2: result.Result = -8; result.Message = "登錄賬號已刪除"; break; case -3: result.Result = -9; result.Message = "登錄密碼輸入錯誤"; break; case -4: result.Result = -10; result.Message = "登錄賬號不存在"; break; } if (loginResult.Result > 0) //登錄成功,值為AccId { result.Result = 0; findCode.IsLogin = true; //更改登錄狀態 result.Message = "成功登錄"; } return result; } }}

6、疑難解答

6.1 #16解答

二維碼中可以加入圖片嗎?文中二維碼 有個圖片上面有 M 字母是怎么處理的?

第一個問題:是把存儲圖片信息存儲到二維碼中,手機掃碼可以識別吧?這個問題涉及到二維碼的存儲容量,理論上如果二維碼的存儲容量足夠大,可把圖片序列化成01的字符進行存儲,掃描就可以識別。但二維碼有不同的標準,不同標準下數據容量是不同的。建議不要存儲圖片,詳情可查看知乎,了解一下:http://www.zhihu.com/question/20387257

M字母是一個圖片,來自http://www.dcloud.io/,只需要把想放的圖放到已生成的二維碼中間即可,但圖片不宜過大,調試一下,用手機識別一下。有興趣的朋友可以查看草榴二維碼:http://cli.im/

6.2 #17解答

疑問: 輸入參數有 用戶名和密碼,那個是每次都需要用戶輸入的?還是通過掃描二維碼獲得的? 還是哪種方式來給 輸入參數的用戶名和密碼賦值的。我想了解樓主是按哪種方式實現的呢?

首先要理解一下掃描登錄的流程,【M】掃描二維碼只獲取相關【B】的唯一標識符信息,掃碼之后,【M】(前提是【M】必須已經登錄成功)發送用戶名\密碼\UUID到【S】進行一系列的驗證;為了提高安全性,在【M】提交數據時,對密碼進行md5時間戳加密。

6.3 #23解答

可以這樣不 在手機端隨機生成碼 加密存在手機上并上傳服務器 后端生成帶有該碼加時間的二維碼 網頁掃的時候對比登陸

要實現掃描登錄,弄懂一個問題:為什么掃描二維碼之后,提交給服務器的數據就是當前頁面所需的呢?在本項目中,是通過SignalR的固有通信connectionid來確認的。你所說的流程應該如下:

微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄

在本流程圖中,比方案中的步驟延長了;在Step2中,會出現問題,如何將【M】推送過來的UUID推送到你看到的【B】端?顯然缺少紐帶。本方法是不可行的。

以上是“微信開發中如何實現基于SignalR的消息推送與二維碼掃描登錄”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

奉新县| 理塘县| 内黄县| 新郑市| 舞钢市| 浮山县| 西吉县| 珠海市| 杨浦区| 新源县| 龙岩市| 大理市| 辽源市| 海南省| 渭南市| 昌黎县| 仙游县| 临清市| 渝北区| 武定县| 丹寨县| 吴旗县| 井研县| 威远县| 扶风县| 富平县| 平江县| 定南县| 化隆| 根河市| 阿尔山市| 元朗区| 潮安县| 铜鼓县| 盐池县| 宾川县| 沙河市| 巩义市| 缙云县| 仁化县| 城固县|