博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于SignalR的消息推送与二维码描登录实现
阅读量:4931 次
发布时间:2019-06-11

本文共 14795 字,大约阅读时间需要 49 分钟。

1 概要说明

    使用微信扫描登录相信大家都不会陌生吧,二维码与手机结合产生了不同应用场景,基于二维码的应用更是比较广泛。为了满足ios、android客户端与web短信平台的结合,特开发了基于SinglarR消息推送机制的扫描登录。本系统涉及到以下知识点:

    SignalR: 这官网,ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程。实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据。

    二维码:使用的QRCode类库,

    MVC5:开发环境是基于MVC5

2、系统关系图

    在实现本功能前,有点不是太确定能否拿下。

    所谓万事开头难,通过查询想资料及自己归纳分析:系统涉及到手机客户端、浏览者、服务端,实现扫描登录也就是三者之间是如何协调工作的。通过axure画出如下关系图:

移动客户端、浏览者、服务端三者协作关系图

    【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…,打开以下界面:

在Browser 标签下输入SignalR,查询到Microsoft.AspNet.SignalR

找到对应的项目,点击“Install”安装按钮即可引用相关类库,同时应用下载相关js库。

    关于SignalR的知识点,可以到官网  进行深入学习。

3.2 服务端SignalR实现

    服务端要向客户端推送UUID,对于UUID唯一标识符,具有重要特性:(1)有时间限制,120秒之内扫码有效;(2)具有一定的状态。对应的声明周期就是:生成—>推送—>状态判断—>手机端扫描—>验证UUID—>状态判断—>销毁等系列过程。

    服务端的核心代码将单独建立一个项目去实现:

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
(); 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{    ///     /// 二维码推送    ///     //[HubName("qrcode")]    public class QRCodeHub : Hub    {        ///         /// 给客户端发送时间间隔        ///         ///         public void SendTimeOutNotice(int time)        {            Clients.Client(Context.ConnectionId).alertClient(time);        }        public void CheckElapsedTime(int time)        {            Clients.Client(Context.ConnectionId).sendElapsedTime(time);        }        ///         /// 发送二维码UUID内容        ///         ///         public void SendQRCodeUUID(string uuid)        {            Clients.Client(Context.ConnectionId).sendQRCodeUUID(uuid);        }        ///         /// Called when the connection connects to this hub instance.        ///         /// 
A
public override Task OnConnected() { SessionTimer.StartTimer(Context.ConnectionId); return base.OnConnected(); } /// /// Called when a connection disconnects from this hub gracefully or due to a timeout. /// /// /// true, if stop was called on the client closing the connection gracefully; /// false, if the connection has been lost for longer than the ///
. /// Timeouts can be caused by clients reconnecting to another SignalR server in scaleout. /// ///
A
public override Task OnDisconnected(bool stopCalled) { SessionTimer.StopTimer(Context.ConnectionId); return base.OnDisconnected(stopCalled); } /// /// Called when the connection reconnects to this hub instance. /// ///
A
public override Task OnReconnected() { if (!SessionTimer.Timers.ContainsKey(Context.ConnectionId)) { SessionTimer.StartTimer(Context.ConnectionId); } return base.OnReconnected(); } /// /// 重置时钟 /// public void ResetTimer() { SessionTimer timer; if (SessionTimer.Timers.TryGetValue(Context.ConnectionId, out timer)) { timer.ResetTimer(); } else { SessionTimer.StartTimer(Context.ConnectionId); } } /// /// 发送普通消息 /// /// /// 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    {        ///         /// 存储客户端对应的Timer        ///         public static readonly ConcurrentDictionary
Timers; private readonly Timer _timer; static SessionTimer() { Timers = new ConcurrentDictionary
(); } ///
/// 构造函数 /// ///
private SessionTimer(string connectionId) { ConnectionId = connectionId; _timer = new Timer { Interval = Utility.ActivityTimerInterval() }; _timer.Elapsed += (s, e) => MonitorElapsedTime(); _timer.Start(); } public int TimeCount { get; set; } ///
/// 客户端连接Id /// public string ConnectionId { get; set; } ///
/// 启动Timer /// ///
public static void StartTimer(string connectionId) { var newTimer = new SessionTimer(connectionId); if (!Timers.TryAdd(connectionId, newTimer)) { newTimer.Dispose(); } } ///
/// 停止Timer /// ///
public static void StopTimer(string connectionId) { SessionTimer oldTimer; if (Timers.TryRemove(connectionId, out oldTimer)) { oldTimer.Dispose(); } } ///
/// 重置Timer /// 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(); } ///
/// 给客户端发送消息 /// 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;        ///         /// 时间间隔        ///         /// 
public static int TimerValue() { return 1000; } public static double ActivityTimerInterval() { return 1000.0; } /// /// 获取当前UUID /// ///
public static string GetUUID(string connectionId) { try { var model = new QRCodeAction().GetValidModel(connectionId); return model.ToJson(connectionId); } catch { return "ERROR"; } } /// /// 删除过期UUID /// 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实体

    所有文件,可在《6、相关文件》中下载。

3.3 客户端SignalR实现

    添加SignalR js库:

    

    两者必须都引用。

    调用接口如下:

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、二维码生成

    二维码类库选择

    添加script标签:

    

    定义div标签,用来呈现二维码:

  呈现二维码:

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

    通过3与4,可实现具有120秒生命周期二维码的生成,对于不同的浏览者,生成的二维码是不同的,效果如下:

5、扫描认证接口

    为了满足【M】端扫描之后,提交UUID+用户信息进行认证,建立QRCode API接口。接口任务比较简单,就是对UUID合法性进行判断,然后判断用户信息登录情况,更改UUID的登录状态。

5.1 输入参数

using Abp.Application.Services.Dto;using System;using System.ComponentModel.DataAnnotations;namespace TxSms.Inputs{    ///     /// 二维码登录认证    ///     [Serializable]    public class QRCodeVerifyInput : IInputDto    {        ///         /// 构造函数        ///         public QRCodeVerifyInput()        {            ConnectionId = Guid.Empty.ToString();            UUID = Guid.Empty;            UserName = Password = "";        }        ///         /// 当前回话ID        ///         [DisplayFormat(ConvertEmptyStringToNull = false)]        public string ConnectionId { get; set; }        ///         /// 唯一标识符号        ///         public Guid UUID { get; set; }        ///         /// 用户账号        ///         [DisplayFormat(ConvertEmptyStringToNull = false)]        public string UserName { get; set; }        ///         /// 登录密码        ///         [DisplayFormat(ConvertEmptyStringToNull = false)]        public string Password { get; set; }        ///         /// 平台        ///         [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{    ///     /// 输出基类    ///     [ModelBinder(typeof(EmptyStringModelBinder))]    public class TxSmsOutputDto : IOutputDto    {        ///         /// 构造函数        ///         public TxSmsOutputDto()        {            Result = 0; //默认为0,表示初始值或正确            Message = "";        }        ///         /// 错误代码        ///         [JsonProperty("Result")]        public int Result { get; set; }        ///         /// 错误信息        ///         [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{    ///     /// 二维码接口    ///     public class QRCodeController : TxSmsApiController    {        ///         /// 二维码登录认证        ///         /// 
/// 0:登录成功;-1:参数错误 -2:ConnectionId、UUID、UserName、Password不允许为空-3:ConnectionId回话id不存在-4:UUID输入错误-5:UUID已过期 /// -6:本UUID已登录-7:登录账号已停用-8:登录账号已删除-9:登录密码输入错误-10:登录账号不存在 ///
[AllowAnonymous] [HttpPost] public async Task
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、总结与下载

    二维码应用比较广泛,记得去北京的故宫旁边的中山公园,里面的古树也有二维码,扫描可查看相关联信息。紧紧对于二维码而言就是存储有限信息,但就是这有限的信息,可以将庞大的信息系统连接一起,所用的应用不是前沿技术的突破,而是我们思考问题方式的转变、思维角度的变化。

    主要文件下载:

文章转自:http://www.cnblogs.com/zsy/p/5882034.html

转载于:https://www.cnblogs.com/dxqNet/p/10276214.html

你可能感兴趣的文章
创建并设置ASP.NET的会话状态服务器(SQL State Server)
查看>>
<metro>Google的验证
查看>>
SQL中NUMERIC和DECIMAL的区别
查看>>
安卓课程设计:微课表
查看>>
Oracle 表的分组操作
查看>>
C#+TaskScheduler(定时任务)实现定时自动下载
查看>>
Lightoj 1007 - Mathematically Hard
查看>>
在OS X上的Intllij Idea中配置GlassFish
查看>>
用查表法快速转换yv12到RGB【转】
查看>>
使用公钥登录SSL
查看>>
实验四 shell 编程(2)
查看>>
Sublime Text 3 搭建 React.js 开发环境
查看>>
hdu 1290_献给杭电五十周年校庆的礼物
查看>>
Nginx 入门
查看>>
openCR-用ROS代码点亮LED的方法
查看>>
豆瓣电影api
查看>>
BufferedInputStream和FileInputStream的区别
查看>>
二阶段之六
查看>>
微博爬虫 python
查看>>
中石油 【递归】普通递归关系
查看>>