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

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的知识点,可以到官网 http://www.asp.net/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<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){//启动SignalRapp.MapSignalR();ConfigureAuth(app);}}
}

3.2.6 其他类库说明

QRCodeAction.cs:维护UUID,创建、保存、状态更改、删除等。

QRModel.cs:UUID实体

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

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

二维码类库选择https://github.com/jeromeetienne/jquery-qrcode

添加script标签:

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

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

<!--二维码登录开始--><div class="ewmcode_login" id="ewmcode_login"><div class="codeText">安全登录 防止被盗</div><div id="divQRCode" class="codebox" style="background:none;"></div><div class="coderemindText">扫一扫登录</div></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
{/// <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、总结与下载

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

主要文件下载:http://files.cnblogs.com/files/zsy/signalr%E4%B8%8Eqrcode.rar

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

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

基于SignalR的消息推送与二维码描登录实现相关推荐

  1. AngularJS+ASP.NET MVC+SignalR实现消息推送

    AngularJS+ASP.NET MVC+SignalR实现消息推送 原文:AngularJS+ASP.NET MVC+SignalR实现消息推送 背景 OA管理系统中,员工提交申请单,消息实时通知 ...

  2. Knative 实战:基于 Kafka 实现消息推送

    作者 | 元毅 阿里云智能事业群高级开发工程师 导读:当前在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何基于 Kafka 实现消息推送呢?本文作者将以阿里云 Kafka 产品为 ...

  3. 基于Python的消息推送(钉钉、微信、QQ)

    文章目录 前言 一.钉钉机器人 1. 新建群聊 2. 添加机器人 3. 设置机器人 4. 程序编写 二.Server酱(微信) 1. 注册登录 2. 获取设置 3. 程序编写 二.Qmsg酱(QQ) ...

  4. 开发一个基于ZXing库以及安卓Studio的二维码扫描小程序(二)

    开发一个基于ZXing库以及安卓Studio的二维码扫描小程序(二) 下面我们做一个ZXing扫描二维码的例子,是通过安卓库的方式引用ZXing应用代码. 开发步骤 建立一个安卓工程(Project) ...

  5. 基于phpqrcode生成带LOGO图标的二维码(源代码例子)

    基于phpqrcode生成带LOGO图标的二维码(源代码例子) <?php //文件输出 include('phpqrcode.php'); // 二维码数据 $data = 'http://w ...

  6. 前端vue uni-app基于uQRCode封装简单快速实用全端二维码生成插件

    快速实现基于uQRCode封装简单快速实用全端二维码生成插件; 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=12677 效果图 ...

  7. Android 应用之二维码扫描登录

    下面介绍二维码扫描登录原理, 首先需要web服务端,和app客户端. web服务端主要工作是生成二维码,检测客户端提交信息正确性,更新网页界面. app客户端主要工作是扫描二维码,提交账户信息(此不是 ...

  8. 聊一聊二维码扫描登录原理

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:2 个月的面试亲身经历告诉大家,如何进入大厂? 扫二维码登录现在比较常见,比如微信.支付宝等 PC 端登录,并且 ...

  9. uniapp中qrcode生成二维码后传的参数不见了_二维码扫描登录,你必须知道的 3 件事...

    作者 | 互联网平头哥 本文经授权转载自互联网平头哥(ID:it_pingtouge) 扫二维码登录现在比较常见,比如微信.支付宝等 PC 端登录,并且好像每款 APP 都支持扫码登录,不搞个扫码登录 ...

最新文章

  1. 一脸懵逼学习Hive的元数据库Mysql方式安装配置
  2. nagios二次开发(一)---开发思想
  3. J-Link 输出供电问题
  4. 服务器损坏mysql修复_云服务器mysql数据库损坏修复mysql
  5. mysql update column_MySQL8.0 新特性:Partial Update of LOB Column
  6. python CV2裁剪图片并保存
  7. IntelliJ IDEA 2019.3 正式发布,给我们带来哪些新特性?| CSDN 博文精选
  8. 虚拟主机搬迁服务器要重新备案吗,域名更换虚拟主机要重新备案吗
  9. JavaScript中this指针的绑定规则
  10. java命令_Java程序员,不得不会的JDK jstack命令工具
  11. 修改pip默认安装位置
  12. mysql 1556_mysqldump: Got error: 1556: You can't use locks with log tables.解决办法
  13. Html5面试问题总结(精华)
  14. 评论:Dremel 3D打印机和HP Sprout的初步印象
  15. 应聘计算机简历中的爱好怎么写,关于求职个人简历中的爱好特长应该怎么写
  16. Mysql数据库insert报慢查询
  17. Pycharm专业版最新版下载安装(社区版和专业版并存)
  18. REST模式:POST,GET,PUT,DELETE,PATCH的含义与区别
  19. Linux中搭建静态网站(练习题)
  20. 深入浅出MFC-读书笔记

热门文章

  1. 使用postman模拟登录请求
  2. 最完美的xslt数值函数与字符串函数(转)
  3. uva 11978 Fukushima Nuclear Blast (二分+多边形与圆交)
  4. login控件“您的登录尝试不成功。请重试”的解决方法
  5. HDOJ2795 Billboard【线段树】
  6. 利用dynamic解决匿名对象不能赋值的问题
  7. 利用cheat engine以及VC编写游戏修改器
  8. Java Lambda表达式forEach无法跳出循环的解决思路
  9. python入门第二天__练习题
  10. 基于I2C总线的0.96寸OLED显示屏驱动