基于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
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的消息推送与二维码描登录实现相关推荐
- AngularJS+ASP.NET MVC+SignalR实现消息推送
AngularJS+ASP.NET MVC+SignalR实现消息推送 原文:AngularJS+ASP.NET MVC+SignalR实现消息推送 背景 OA管理系统中,员工提交申请单,消息实时通知 ...
- Knative 实战:基于 Kafka 实现消息推送
作者 | 元毅 阿里云智能事业群高级开发工程师 导读:当前在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何基于 Kafka 实现消息推送呢?本文作者将以阿里云 Kafka 产品为 ...
- 基于Python的消息推送(钉钉、微信、QQ)
文章目录 前言 一.钉钉机器人 1. 新建群聊 2. 添加机器人 3. 设置机器人 4. 程序编写 二.Server酱(微信) 1. 注册登录 2. 获取设置 3. 程序编写 二.Qmsg酱(QQ) ...
- 开发一个基于ZXing库以及安卓Studio的二维码扫描小程序(二)
开发一个基于ZXing库以及安卓Studio的二维码扫描小程序(二) 下面我们做一个ZXing扫描二维码的例子,是通过安卓库的方式引用ZXing应用代码. 开发步骤 建立一个安卓工程(Project) ...
- 基于phpqrcode生成带LOGO图标的二维码(源代码例子)
基于phpqrcode生成带LOGO图标的二维码(源代码例子) <?php //文件输出 include('phpqrcode.php'); // 二维码数据 $data = 'http://w ...
- 前端vue uni-app基于uQRCode封装简单快速实用全端二维码生成插件
快速实现基于uQRCode封装简单快速实用全端二维码生成插件; 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=12677 效果图 ...
- Android 应用之二维码扫描登录
下面介绍二维码扫描登录原理, 首先需要web服务端,和app客户端. web服务端主要工作是生成二维码,检测客户端提交信息正确性,更新网页界面. app客户端主要工作是扫描二维码,提交账户信息(此不是 ...
- 聊一聊二维码扫描登录原理
点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:2 个月的面试亲身经历告诉大家,如何进入大厂? 扫二维码登录现在比较常见,比如微信.支付宝等 PC 端登录,并且 ...
- uniapp中qrcode生成二维码后传的参数不见了_二维码扫描登录,你必须知道的 3 件事...
作者 | 互联网平头哥 本文经授权转载自互联网平头哥(ID:it_pingtouge) 扫二维码登录现在比较常见,比如微信.支付宝等 PC 端登录,并且好像每款 APP 都支持扫码登录,不搞个扫码登录 ...
最新文章
- 一脸懵逼学习Hive的元数据库Mysql方式安装配置
- nagios二次开发(一)---开发思想
- J-Link 输出供电问题
- 服务器损坏mysql修复_云服务器mysql数据库损坏修复mysql
- mysql update column_MySQL8.0 新特性:Partial Update of LOB Column
- python CV2裁剪图片并保存
- IntelliJ IDEA 2019.3 正式发布,给我们带来哪些新特性?| CSDN 博文精选
- 虚拟主机搬迁服务器要重新备案吗,域名更换虚拟主机要重新备案吗
- JavaScript中this指针的绑定规则
- java命令_Java程序员,不得不会的JDK jstack命令工具
- 修改pip默认安装位置
- mysql 1556_mysqldump: Got error: 1556: You can't use locks with log tables.解决办法
- Html5面试问题总结(精华)
- 评论:Dremel 3D打印机和HP Sprout的初步印象
- 应聘计算机简历中的爱好怎么写,关于求职个人简历中的爱好特长应该怎么写
- Mysql数据库insert报慢查询
- Pycharm专业版最新版下载安装(社区版和专业版并存)
- REST模式:POST,GET,PUT,DELETE,PATCH的含义与区别
- Linux中搭建静态网站(练习题)
- 深入浅出MFC-读书笔记
热门文章
- 使用postman模拟登录请求
- 最完美的xslt数值函数与字符串函数(转)
- uva 11978 Fukushima Nuclear Blast (二分+多边形与圆交)
- login控件“您的登录尝试不成功。请重试”的解决方法
- HDOJ2795 Billboard【线段树】
- 利用dynamic解决匿名对象不能赋值的问题
- 利用cheat engine以及VC编写游戏修改器
- Java Lambda表达式forEach无法跳出循环的解决思路
- python入门第二天__练习题
- 基于I2C总线的0.96寸OLED显示屏驱动