基于SignalR的消息推送與二維碼掃描登錄實(shí)現(xiàn)代碼
1 概要說(shuō)明
使用微信掃描登錄相信大家都不會(huì)陌生吧,二維碼與手機(jī)結(jié)合產(chǎn)生了不同應(yīng)用場(chǎng)景,基于二維碼的應(yīng)用更是比較廣泛。為了滿足ios、android客戶端與web短信平臺(tái)的結(jié)合,特開(kāi)發(fā)了基于SinglarR消息推送機(jī)制的掃描登錄。本系統(tǒng)涉及到以下知識(shí)點(diǎn):
SignalR:http://signalr.net/ 這官網(wǎng),ASP.NET SignalR 是為 ASP.NET 開(kāi)發(fā)人員提供的一個(gè)庫(kù),可以簡(jiǎn)化開(kāi)發(fā)人員將實(shí)時(shí) Web 功能添加到應(yīng)用程序的過(guò)程。實(shí)時(shí) Web 功能是指這樣一種功能:當(dāng)所連接的客戶端變得可用時(shí)服務(wù)器代碼可以立即向其推送內(nèi)容,而不是讓服務(wù)器等待客戶端請(qǐng)求新的數(shù)據(jù)。
二維碼:使用的QRCode類(lèi)庫(kù),https://github.com/jeromeetienne/jquery-qrcode
MVC5:開(kāi)發(fā)環(huán)境是基于MVC5

2、系統(tǒng)關(guān)系圖
在實(shí)現(xiàn)本功能前,有點(diǎn)不是太確定能否拿下。
所謂萬(wàn)事開(kāi)頭難,通過(guò)查詢想資料及自己歸納分析:系統(tǒng)涉及到手機(jī)客戶端、瀏覽者、服務(wù)端,實(shí)現(xiàn)掃描登錄也就是三者之間是如何協(xié)調(diào)工作的。通過(guò)axure畫(huà)出如下關(guān)系圖:

移動(dòng)客戶端、瀏覽者、服務(wù)端三者協(xié)作關(guān)系圖
【M】:表示移動(dòng)端 【B】:表示瀏覽者(瀏覽器客戶端) 【S】:服務(wù)端,消息推送者及掃描認(rèn)證接口發(fā)布者
步驟說(shuō)明:
Step(步驟)1 ,【B】瀏覽登錄頁(yè)面,Step2【S】產(chǎn)生一個(gè)標(biāo)識(shí)符UUID,并推送給B,生成登錄二維碼;
Step3,【M】掃描二維碼,前提條件是【M】已登錄,Step4【M】解析二維碼信息獲取UUID;
Step5,【M】向服務(wù)端發(fā)送UUID+登錄信息,Step6【S】對(duì)UUID+登錄信息進(jìn)行相關(guān)解析認(rèn)證,Step6 UUID認(rèn)證,不通過(guò)認(rèn)證,則到Step6-1 重新生成UUID循環(huán)Step 2與并Step6-2 返回給【M】UUID認(rèn)證失敗原因,Step6 通過(guò)認(rèn)證,Step6-2轉(zhuǎn)到登錄信息認(rèn)證,Step 7登錄信息認(rèn)證,失敗Step7-3重新生成UUID循環(huán)Step 2,成功則Step7-1推送給【B】跳轉(zhuǎn)到首頁(yè)。
3、SignalR循環(huán)消息推送
3.1 引用SignalR
由于本人用的是VS15Preview4,可以直接使用Nuget可視化管理工具進(jìn)行安裝:Tools—>Nuget Package Manager—>Manage Nuget Packages for Solution…,打開(kāi)以下界面:

在Browser 標(biāo)簽下輸入SignalR,查詢到Microsoft.AspNet.SignalR
/p>
找到對(duì)應(yīng)的項(xiàng)目,點(diǎn)擊“Install”安裝按鈕即可引用相關(guān)類(lèi)庫(kù),同時(shí)應(yīng)用下載相關(guān)js庫(kù)。
關(guān)于SignalR的知識(shí)點(diǎn),可以到官網(wǎng) http://www.asp.net/signalr 進(jìn)行深入學(xué)習(xí)。
3.2 服務(wù)端SignalR實(shí)現(xiàn)
服務(wù)端要向客戶端推送UUID,對(duì)于UUID唯一標(biāo)識(shí)符,具有重要特性:(1)有時(shí)間限制,120秒之內(nèi)掃碼有效;(2)具有一定的狀態(tài)。對(duì)應(yīng)的聲明周期就是:生成—>推送—>狀態(tài)判斷—>手機(jī)端掃描—>驗(yàn)證UUID—>狀態(tài)判斷—>銷(xiāo)毀等系列過(guò)程。
服務(wù)端的核心代碼將單獨(dú)建立一個(gè)項(xiàng)目去實(shí)現(xiàn):

3.2.1 Nofifier.cs通知類(lèi)
本類(lèi)將連接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核心實(shí)現(xiàn)
SignalR的核心代碼:
using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;
namespace TxSms.SingalR
{
/// <summary>
/// 二維碼推送
///
</summary>
//[HubName("qrcode")] public class QRCodeHub : Hub {
/// <summary> /// 給客戶端發(fā)送時(shí)間間隔 ///
</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>
/// 發(fā)送二維碼UUID內(nèi)容
///
</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> /// 重置時(shí)鐘 ///
</summary> public void ResetTimer()
{ SessionTimer timer;
if (SessionTimer.Timers.TryGetValue(Context.ConnectionId, out timer))
{ timer.ResetTimer();
}
else
{
SessionTimer.StartTimer(Context.ConnectionId);
}
} ///
<summary> ///
發(fā)送普通消息 ///
</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 對(duì)應(yīng)客戶端時(shí)鐘
對(duì)【B】來(lái)說(shuō),產(chǎn)生一個(gè)獨(dú)立的timer,進(jìn)行按1s間隔發(fā)送消息。
using System;using System.Collections.Concurrent;using System.Timers;namespace TxSms.SingalR{ public class SessionTimer : IDisposable
{
///
<summary> /// 存儲(chǔ)客戶端對(duì)應(yīng)的Timer ///
</summary>
public static readonly ConcurrentDictionary<string, SessionTimer> Timers; private readonly Timer _timer; static SessionTimer()
{ Timers = new ConcurrentDictionary<string, SessionTimer>();
}
/// <summary> /// 構(gòu)造函數(shù) /// </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> /// 啟動(dòng)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> ///
給客戶端發(fā)送消息 ///
</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 基礎(chǔ)配置
滿足時(shí)鐘、獲取QRCode等
using TxSms.Actions;namespace TxSms.SingalR
{
internal class Utility { public static int IntNum = 0; ///
<summary> /// 時(shí)間間隔 ///
</summary> ///
<returns></returns>
public static int TimerValue()
{ return 1000;
}
public static double ActivityTimerInterval()
{ return 1000.0;
}
/// <summary> /// 獲取當(dāng)前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> /// 刪除過(guò)期UUID ///
</summary> public static void ClearExpiredUUID() { IntNum++;
if (IntNum <= 1000) return;
new QRCodeAction().ClearExpiredUUID();
IntNum = 0; } }}
3.2.5 SignalR在MVC中啟動(dòng)配置
在MVC中,啟動(dòng)項(xiàng)目進(jìn)行如下配置:
using Microsoft.Owin;using Owin;
[assembly: OwinStartup(typeof(TxSms.Web.Startup))]namespace TxSms.Web{ public partial class Startup
{
public void Configuration(IAppBuilder app)
{ //啟動(dòng)SignalR app.MapSignalR(); ConfigureAuth(app);
} }}
3.2.6 其他類(lèi)庫(kù)說(shuō)明
QRCodeAction.cs:維護(hù)UUID,創(chuàng)建、保存、狀態(tài)更改、刪除等。
QRModel.cs:UUID實(shí)體
所有文件,可在《7、總結(jié)與下載》中下載。
3.3 客戶端SignalR實(shí)現(xiàn)
添加SignalR js庫(kù):
<script type="text/javascript" src="~/Scripts/jquery.signalR-2.2.1.min.js"> </script> <script type="text/javascript" src="~/signalr/hubs"> </script
兩者必須都引用。
調(diào)用接口如下:
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());
});
});
以上代碼包括相關(guān)二維碼的生成。
4、二維碼的生成與存儲(chǔ)數(shù)據(jù)解析
4.1 二維碼的生成
二維碼類(lèi)庫(kù)選擇https://github.com/jeromeetienne/jquery-qrcode 一個(gè)QRCode原生態(tài)js類(lèi)庫(kù),jquery對(duì)其進(jìn)行了擴(kuò)展。
添加script標(biāo)簽:
<script type="text/javascript" src="~/Scripts/qrcode.min.js"> </script> <script type="text/javascript" src="~/Scripts/jquery.qrcode.min.js"> </script>
定義div標(biāo)簽,用來(lái)呈現(xiàn)二維碼:
<!--二維碼登錄開(kāi)始--> <div> <div>安全登錄 防止被盜</div> <div> </div> <div>掃一掃登錄</div> </div> <!--二維碼登錄結(jié)束-->
呈現(xiàn)二維碼:
$("#divQRCode").html("");
$('#divQRCode').qrcode({ width: 180, height: 180, correctLevel: 0, text: codeUUID });
通過(guò)3與4,可實(shí)現(xiàn)具有180秒生命周期二維碼的生成,對(duì)于不同的瀏覽者,生成的二維碼是不同的,效果如下:

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


hbuilder官網(wǎng)
千牛電腦客戶端二維碼登錄界面
顯然,掃描這兩個(gè)圖片上的二維碼會(huì)得到不同的結(jié)果。對(duì)某些二維碼的解碼要對(duì)應(yīng)配套的客戶端才能起到作用,否則用其他工具解析出來(lái)也就是字符串。
在本系統(tǒng)中,二維碼存儲(chǔ)的是一個(gè)json對(duì)象,格式為:
{"connectionid":"19c12e95-26d7-410c-8292-2a3afdd1a4da","uuid":"a04702df-6a52-4e1c-be8b-9b3dbeef4d72","islogin":0,"isvalid":1}
connectionid:客戶端與SignalR聯(lián)系的id,其格式為Guid
uuid:對(duì)應(yīng)connectionid產(chǎn)生的一個(gè)唯一標(biāo)識(shí)符,其格式為Guidislogin:當(dāng)前connectionid連接是否已登錄,1—>表示登錄,0—>未登錄isvalid:當(dāng)前connectionid對(duì)應(yīng)的uuid是否有效,1—>表示有效,0—>表示失效
手機(jī)客戶端掃描之后,可根據(jù)這些參數(shù)情況進(jìn)行判斷,是否向服務(wù)端發(fā)送請(qǐng)求。在做掃描應(yīng)用(比如掃描登錄)時(shí),要依據(jù)業(yè)務(wù)場(chǎng)景進(jìn)行消息傳遞,生成對(duì)應(yīng)二維碼,并不局限于json對(duì)象、url地址等。
總結(jié)下來(lái),二維碼應(yīng)用場(chǎng)景,如下圖:

5、掃描認(rèn)證接口
為了滿足【M】端掃描之后,提交UUID+用戶信息進(jìn)行認(rèn)證,建立QRCode API接口。接口任務(wù)比較簡(jiǎn)單,就是對(duì)UUID合法性進(jìn)行判斷,然后判斷用戶信息登錄情況,更改UUID的登錄狀態(tài)。
5.1 輸入?yún)?shù)
using Abp.Application.Services.Dto;using System;using System.ComponentModel.DataAnnotations;
namespace TxSms.Inputs{ /// <summary> /// 二維碼登錄認(rèn)證 /// </summary> [Serializable]
public class QRCodeVerifyInput : IInputDto { /// <summary> /// 構(gòu)造函數(shù) /// </summary>
public QRCodeVerifyInput() { ConnectionId = Guid.Empty.ToString();
UUID = Guid.Empty; UserName = Password = ""; } /// <summary> /// 當(dāng)前回話ID /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)] public string ConnectionId { get; set;
} /// <summary> /// 唯一標(biāo)識(shí)符號(hào) /// </summary>
public Guid UUID { get; set; } /// <summary> /// 用戶賬號(hào) /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string UserName { get; set; } /// <summary> /// 登錄密碼 /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Password { get; set; } /// <summary> /// 平臺(tái) /// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Platform { get; set;
} }}
5.2 輸出參數(shù)
using Abp.Application.Services.Dto;
using Newtonsoft.Json;using System.ComponentModel.DataAnnotations;using System.Web.Mvc;using TxSms.MVC;namespace TxSms.Outputs{ /// <summary> /// 輸出基類(lèi) /// </summary> [ModelBinder(typeof(EmptyStringModelBinder))] public class TxSmsOutputDto : IOutputDto { /// <summary> /// 構(gòu)造函數(shù) ///
</summary> public TxSmsOutputDto()
{ Result = 0; //默認(rèn)為0,表示初始值或正確 Message = "";
} /// <summary> /// 錯(cuò)誤代碼 ///
</summary> [JsonProperty("Result")] public int Result { get; set; } /// <summary> /// 錯(cuò)誤信息 /// </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> /// 二維碼登錄認(rèn)證 /// </summary> /// <returns> /// 0:登錄成功;-1:參數(shù)錯(cuò)誤 -2:ConnectionId、UUID、UserName、Password不允許為空-3:ConnectionId回話id不存在-4:UUID輸入錯(cuò)誤-5:UUID已過(guò)期 /// -6:本UUID已登錄-7:登錄賬號(hào)已停用-8:登錄賬號(hào)已刪除-9:登錄密碼輸入錯(cuò)誤-10:登錄賬號(hào)不存在 /// </returns> [AllowAnonymous] [HttpPost] public async Task<TxSmsOutputDto> QRCodeVerify([FromBody]QRCodeVerifyInput model) { TxSmsOutputDto result = new TxSmsOutputDto(); #region 參數(shù)驗(yàn)證 if (model.IsNull()) { result.Result = -1; result.Message = "參數(shù)錯(cuò)誤"; 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 參數(shù)驗(yàn)證 #region 有效性判斷 //驗(yàn)證ConnectionId合法性 if (QRCodeAction.QRCodeLists.ContainsKey(model.ConnectionId)) { result.Result = -3; result.Message = "ConnectionId回話id不存在"; return result; } //驗(yàn)證UUID有效性 var findCode = QRCodeAction.QRCodeLists[model.ConnectionId]; if (!model.UUID.Equals(findCode.UUID)) { result.Result = -4; result.Message = "UUID輸入錯(cuò)誤"; return result; } if (!findCode.IsValid()) { result.Result = -5; result.Message = "UUID已過(guò)期"; 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 = "登錄賬號(hào)已停用"; break; case -2: result.Result = -8; result.Message = "登錄賬號(hào)已刪除"; break; case -3: result.Result = -9; result.Message = "登錄密碼輸入錯(cuò)誤"; break; case -4: result.Result = -10; result.Message = "登錄賬號(hào)不存在"; break; } if (loginResult.Result > 0) //登錄成功,值為AccId { result.Result = 0; findCode.IsLogin = true; //更改登錄狀態(tài) result.Message = "成功登錄"; } return result; } }}
6、疑難解答
6.1 #16解答
二維碼中可以加入圖片嗎?文中二維碼 有個(gè)圖片上面有 M 字母是怎么處理的?
第一個(gè)問(wèn)題:是把存儲(chǔ)圖片信息存儲(chǔ)到二維碼中,手機(jī)掃碼可以識(shí)別吧?這個(gè)問(wèn)題涉及到二維碼的存儲(chǔ)容量,理論上如果二維碼的存儲(chǔ)容量足夠大,可把圖片序列化成01的字符進(jìn)行存儲(chǔ),掃描就可以識(shí)別。但二維碼有不同的標(biāo)準(zhǔn),不同標(biāo)準(zhǔn)下數(shù)據(jù)容量是不同的。建議不要存儲(chǔ)圖片,詳情可查看知乎,了解一下:http://www.zhihu.com/question/20387257
M字母是一個(gè)圖片,來(lái)自http://www.dcloud.io/,只需要把想放的圖放到已生成的二維碼中間即可,但圖片不宜過(guò)大,調(diào)試一下,用手機(jī)識(shí)別一下。有興趣的朋友可以查看草榴二維碼:http://cli.im/
6.2 #17解答
疑問(wèn): 輸入?yún)?shù)有 用戶名和密碼,那個(gè)是每次都需要用戶輸入的?還是通過(guò)掃描二維碼獲得的? 還是哪種方式來(lái)給 輸入?yún)?shù)的用戶名和密碼賦值的。我想了解樓主是按哪種方式實(shí)現(xiàn)的呢?
首先要理解一下掃描登錄的流程,【M】掃描二維碼只獲取相關(guān)【B】的唯一標(biāo)識(shí)符信息,掃碼之后,【M】(前提是【M】必須已經(jīng)登錄成功)發(fā)送用戶名\密碼\UUID到【S】進(jìn)行一系列的驗(yàn)證;為了提高安全性,在【M】提交數(shù)據(jù)時(shí),對(duì)密碼進(jìn)行md5時(shí)間戳加密。
6.3 #23解答
可以這樣不 在手機(jī)端隨機(jī)生成碼 加密存在手機(jī)上并上傳服務(wù)器 后端生成帶有該碼加時(shí)間的二維碼 網(wǎng)頁(yè)掃的時(shí)候?qū)Ρ鹊顷?/p>
要實(shí)現(xiàn)掃描登錄,弄懂一個(gè)問(wèn)題:為什么掃描二維碼之后,提交給服務(wù)器的數(shù)據(jù)就是當(dāng)前頁(yè)面所需的呢?在本項(xiàng)目中,是通過(guò)SignalR的固有通信connectionid來(lái)確認(rèn)的。你所說(shuō)的流程應(yīng)該如下:

在本流程圖中,比方案中的步驟延長(zhǎng)了;在Step2中,會(huì)出現(xiàn)問(wèn)題,如何將【M】推送過(guò)來(lái)的UUID推送到你看到的【B】端?顯然缺少紐帶。本方法是不可行的。
7、總結(jié)與下載
二維碼應(yīng)用比較廣泛,記得去北京的故宮旁邊的中山公園,里面的古樹(shù)也有二維碼,掃描可查看相關(guān)聯(lián)信息。緊緊對(duì)于二維碼而言就是存儲(chǔ)有限信息,但就是這有限的信息,可以將龐大的信息系統(tǒng)連接一起,所用的應(yīng)用不是前沿技術(shù)的突破,而是我們思考問(wèn)題方式的轉(zhuǎn)變、思維角度的變化。由于二維碼具有信息存儲(chǔ)的獨(dú)特性,可在以下方面應(yīng)用:
- 信息獲取(名片、地圖、WIFI密碼、資料)
- 網(wǎng)站跳轉(zhuǎn)(跳轉(zhuǎn)到微博、手機(jī)網(wǎng)站、網(wǎng)站)
- 廣告推送(用戶掃碼,直接瀏覽商家推送的視頻、音頻廣告)
- 手機(jī)電商(用戶掃碼、手機(jī)直接購(gòu)物下單)
- 防偽溯源(用戶掃碼、即可查看生產(chǎn)地;同時(shí)后臺(tái)可以獲取最終消費(fèi)地)優(yōu)惠促銷(xiāo)(用戶掃碼,下載電子優(yōu)惠券,抽獎(jiǎng))
- 會(huì)員管理(用戶手機(jī)上獲取電子會(huì)員信息、VIP服務(wù))
- 手機(jī)支付(掃描商品二維碼,通過(guò)銀行或第三方支付提供的手機(jī)端通道完成支付)
由于最近在做短信業(yè)務(wù)平臺(tái),將二維碼應(yīng)用到營(yíng)銷(xiāo)管理中,每個(gè)業(yè)務(wù)人員具有獨(dú)立的推廣二維碼,客戶掃碼可進(jìn)行短信測(cè)試,若注冊(cè)成為會(huì)員則就是本業(yè)務(wù)人員的直屬客戶,可查看《二維碼在短信業(yè)務(wù)應(yīng)用的初步構(gòu)思》。
最后,上傳《基于SignalR的消息推送與二維碼描登錄實(shí)現(xiàn)》主要文件下載:signalrqrcode
- Asp.NET MVC中使用SignalR實(shí)現(xiàn)推送功能
- 使用SignalR推送服務(wù)在Android的實(shí)現(xiàn) SignalA
- asp.net mvc實(shí)現(xiàn)簡(jiǎn)單的實(shí)時(shí)消息推送
- ASP.NET實(shí)現(xiàn)推送文件到瀏覽器的方法
- .net平臺(tái)推送ios消息的實(shí)現(xiàn)方法
- .net 通過(guò)URL推送POST數(shù)據(jù)具體實(shí)現(xiàn)
- SignalR Self Host+MVC等多端消息推送服務(wù)(二)
- SignalR Self Host+MVC等多端消息推送服務(wù)(一)
- SignalR Self Host+MVC等多端消息推送服務(wù)(三)
相關(guān)文章
.Net中Task Parallel Library的進(jìn)階用法
這篇文章介紹了.Net中Task Parallel Library的進(jìn)階用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10
asp.net靜態(tài)方法彈出對(duì)話框?qū)崿F(xiàn)思路
為菜鳥(niǎo)所準(zhǔn)備……其實(shí)就是彈出JavaScript小窗口,總得來(lái)說(shuō)就是定義的一個(gè)DIV,感興趣的朋友可以了解下,或許對(duì)你學(xué)習(xí)asp.net有所幫助2013-02-02
收集學(xué)習(xí)asp.net比較完整的面向?qū)ο箝_(kāi)發(fā)流程
如果你已經(jīng)有較多的面向?qū)ο箝_(kāi)發(fā)經(jīng)驗(yàn),跳過(guò)以下這兩步 第一步:掌握一門(mén).NET面向?qū)ο笳Z(yǔ)言第二步:對(duì).NET Framework類(lèi)庫(kù)有一定的了解;在具備了OO基礎(chǔ)之后,以下是具體的學(xué)習(xí)ASP.NET技術(shù)步驟2012-12-12
ASP.NET Core應(yīng)用錯(cuò)誤處理之DeveloperExceptionPageMiddleware中間件呈現(xiàn)“開(kāi)發(fā)者
這篇文章主要給大家介紹了關(guān)于ASP.NET Core應(yīng)用錯(cuò)誤處理之DeveloperExceptionPageMiddleware中間件呈現(xiàn)“開(kāi)發(fā)者異常頁(yè)面”的相關(guān)資料,需要的朋友可以參考下2019-01-01
ashx介紹以及ashx文件與aspx文件之間的區(qū)別
這篇文章主要介紹了ashx以及ashx文件與aspx文件之間的區(qū)別。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12
ASP.NET(VB)寫(xiě)的后臺(tái)發(fā)送短信實(shí)現(xiàn)代碼
使用vb寫(xiě)的后臺(tái)發(fā)送短信代碼,很實(shí)用的一項(xiàng)功能,感興趣的朋友可以了解下,或許對(duì)你學(xué)習(xí)asp.net vb有所幫助2013-02-02
asp.net Parameters.AddWithValue方法在SQL語(yǔ)句的 Where 字句中的用法
今天晚上看論壇,有人提問(wèn)說(shuō),Parameters.AddWithValue方法在有些情況下不好使2009-01-01
如何使用Rotativa在ASP.NET Core MVC中創(chuàng)建PDF詳解
這篇文章主要給大家介紹了關(guān)于如何使用Rotativa在ASP.NET Core MVC中創(chuàng)建PDF的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02

