ASP.NET Core & 双因素验证2FA 实战经验分享
必读
本文源码核心逻辑使用AspNetCore.Totp,为什么不使用AspNetCore.Totp而是使用源码封装后面将会说明。
为了防止不提供原网址的转载,特在这里加上原文链接:
https://www.cnblogs.com/yuefengkai/p/11408339.html
双因素认证
双因素身份认证就是通过你所知道再加上你所能拥有的这二个要素组合到一起才能发挥作用的身份认证系统。双因素认证是一种采用时间同步技术的系统,采用了基于时间、事件和密钥三变量而产生的一次性密码来代替传统的静态密码。每个动态密码卡都有一个唯一的密钥,该密钥同时存放在服务器端,每次认证时动态密码卡与服务器分别根据同样的密钥,同样的随机参数(时间、事件)和同样的算法计算了认证的动态密码,从而确保密码的一致性,从而实现了用户的认证。就像我们去银行办卡送的口令牌.

一. 前言
最近公司内部SSO登录一直在找一种安全的方式,目前已实现方案:账号密码登录以及手机验证码登录,通过Apollo切换不同的登录方式,想起18年看到AspNetCore.Totp并也编写了DemodotNetCore-2FA登录,将之前写的再完善并且在此记录和分析,希望对大家有些帮助。
二. AspNetCore.Totp
说明一下为什么要用AspNetCore.Totp修改并且重新打包Brook.Totp因AspNetCore.Totp在生成二维码链接时会访问404(google.com)网站,国内基本无法使用,这很不清真,还有就是注入需要注入接口和实现类,使用起来很繁琐,所以才萌生了让使用起来更方便,并且不依赖Google生成二维码
- 生成二维码
accountIdentity = accountIdentity.Replace(" ", "");
            var encodedSecretKey = Base32.Encode(accountSecretKey);
            var provisionUrl = UrlEncoder.Encode(string.Format("otpauth://totp/{0}?secret={1}&issuer={2}", accountIdentity, encodedSecretKey, UrlEncoder.Encode(issuer)));
            var protocol = useHttps ? "https" : "http";
            var url = $"{protocol}://chart.googleapis.com/chart?cht=qr&chs={qrCodeWidth}x{qrCodeHeight}&chl={provisionUrl}";
            var totpSetup = new TotpSetup
            {
                QrCodeImage = this.GetQrImage(url),
                ManualSetupKey = encodedSecretKey
            };
- 注入方式
 Startup注入
services.AddSingleton<ITotpSetupGenerator, TotpSetupGenerator>();
services.AddSingleton<ITotpValidator, TotpValidator>();
services.AddSingleton<ITotpGenerator, TotpGenerator>();
Controller注入
 private readonly ITotpGenerator _totpGenerator;
        private readonly ITotpSetupGenerator _totpSetupGenerator;
        private readonly ITotpValidator _totpValidator;
        public ValuesController(ITotpSetupGenerator totpSetupGenerator)
        {
            _totpSetupGenerator = totpSetupGenerator;
            _totpGenerator = new TotpGenerator();
            _totpValidator = new TotpValidator(_totpGenerator);
        }
三. Brook.Totp
- 二维码使用QRCoder来生成,不依赖外部网络
        /// <summary>
        /// 生成二维码
        /// </summary>
        /// <param name="provisionUrl"></param>
        /// <param name="pixelsPerModule"></param>
        /// <returns></returns>
        private string GetQrBase64Imageg(string provisionUrl,int pixelsPerModule)
        {
            QRCodeGenerator qrGenerator = new QRCodeGenerator();
            QRCodeData qrCodeData = qrGenerator.CreateQrCode(provisionUrl, QRCodeGenerator.ECCLevel.Q);
            Base64QRCode qrCode = new Base64QRCode(qrCodeData);
            string qrCodeImageAsBase64 = qrCode.GetGraphic(2);
            return  $"data:image/png;base64,{qrCodeImageAsBase64}";
        }
- 注入方式
 Startup注入
services.AddBrookTotp();
Controller注入
private readonly ITotp _totp;
public AccountController(ITotp totp)
{
        _totp = totp;
}
四.双因素APP
推荐使用Microsoft Authenticator支持IOS、安卓可自动备份
之前使用Google Authenticator手机坏了 Gitlab和DropBox 再也进不去了(心疼自己三秒钟)
五. 完整流程效果图
使用Microsoft Authenticator

- 正常登录
  
- 登录成功后绑定
  
- 绑定后再次登录
  
六.如何使用
所有源代码请参照我的GitHub https://github.com/yuefengkai/Brook.Totp
Demo中使用了
- EF Core In Memory Database所有的数据只存在内存中
- Cache in-memory
- dotNET Core Authentication
下方只展示部分代码
- 新建netCoreMVC项目添加Nuget包Brook.Totp
  
- Startup注入
services.AddMemoryCache();
services.AddSingleton<ICacheManage, CacheManage>();
services.AddBrookTotp();
services.AddDbContext<BrookTotpDBContext>(options => options.UseInMemoryDatabase(databaseName: "BrookTotpDB"));
- Controller使用
private readonly ITotp _totp;
public AccountController(ITotp totp)
{
        _totp = totp;
}
//获取二维码
[Authorize]
public IActionResult GetQr()
{
    var totpSetup = _totp.GenerateUrl("dotNETBuild", CurremtUser.Email, CurremtUser.SecretKeyFor2FA);
    return Json(new { qrCodeContennt = totpSetup.QrCodeImageContent });
}
//验证双因素校验码
[Authorize]
[HttpPost]
public async Task<IActionResult> Valid(int code)
{
    var valid = _totp.Validate(CurremtUser.SecretKeyFor2FA
        , code, 30);
    if (!valid)
    {
        return Json(new { result = 0, msg = "2FA校验失败" });
    }
    //校验成功后 如果是第一次绑定校验 需将用户的accountSecretKey 存入数据库
    CurremtUser.IsOpen2FA = true;
    await _userService.UpdateAsync(CurremtUser);
    _cacheManage.Remove(string.Format(CacheKeys.GetUserForEmail, CurremtUser.Email));
    var claims = new List<Claim>
    {
        new Claim("user", CurremtUser.Email),
        new Claim("role", "Member")
    };
    await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "role")));
    return Json(new { result = 1, msg = "2FA校验成功", url = "/Home/Index" });
}
七.写在最后
以上所有源代码已开源在 https://github.com/yuefengkai/Brook.Totp
如果觉得有用请给我个Start!
作者:Brook(高增智)
ASP.NET Core & 双因素验证2FA 实战经验分享的更多相关文章
- 《ASP.NET Core技术内幕与项目实战》精简集-目录
		本系列是杨中科2022年最新作品<ASP.NET Core技术内幕与项目实战>及B站配套视频(强插点赞)的精简集,是一个读书笔记.总结和提炼了主要知识点,遵守代码优先原则,以利于快速复习和 ... 
- 自动化CodeReview - ASP.NET Core请求参数验证
		自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 参数验证实现 在做服务端开发 ... 
- 使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)——第1部分
		原文:使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)--第1部分 原文链接:https://www.codeproject.com/Articles/5160941/ASP- ... 
- 简读《ASP.NET Core技术内幕与项目实战》之3:配置
		特别说明:1.本系列内容主要基于杨中科老师的书籍<ASP.NET Core技术内幕与项目实战>及配套的B站视频视频教程,同时会增加极少部分的小知识点2.本系列教程主要目的是提炼知识点,追求 ... 
- 快读《ASP.NET Core技术内幕与项目实战》EFCore2.5:集合查询原理揭秘(IQueryable和IEnumerable)
		本节内容,涉及4.6(P116-P130).主要NuGet包:如前述章节 一.LINQ和EFCore的集合查询扩展方法的区别 1.LINQ和EFCore中的集合查询扩展方法,虽然命名和使用完全一样,都 ... 
- 快读《ASP.NET Core技术内幕与项目实战》WebApi3.1:WebApi最佳实践
		本节内容,涉及到6.1-6.6(P155-182),以WebApi说明为主.主要NuGet包:无 一.创建WebApi的最佳实践,综合了RPC和Restful两种风格的特点 1 //定义Person类 ... 
- 第9期Unity User Group Beijing图文报道:《Unity实战经验分享》
		时间来到了金秋九月,北京UUG活动也来到了第九期.本次活动的主题为<Unity实战经验分享>,为此我们邀请了3位资深的行业大神.这次我们仍然在北京市海淀区丹棱街5号微软大厦举行活动,在这里 ... 
- Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码)
		Visual Studio 2015开发Qt项目实战经验分享(附项目示例源码) 转 https://blog.csdn.net/lhl1124281072/article/details/800 ... 
- ASP.NET Core 使用Cookie验证身份
		ASP.NET Core 1.x提供了通过Cookie 中间件将用户主体序列化为一个加密的Cookie,然后在后续请求中验证Cookie并重新创建主体,并将其分配给HttpContext.User属性 ... 
随机推荐
- jmeter待解决55大问题
			客户交付一个性能测试项目,阐述实施流程. 解释5个常用的性能指标的名称与具体含义. 写出5个jmeter中常用函数,并对其中2个举例说明用法. 简述jmeter的工作原理? 什么是集合点?设置集合点有 ... 
- nRF51822 的两路 PWM 极性
			忙了一阵这个PWM,玩着玩着终于发现了些规律.Nordic 也挺会坑爹的. nRF51822 是没有硬件 PWM 的,只能靠一系列难以理解的 PPI /GPIOTE/TIMER来实现,其实我想说,我醉 ... 
- Koa Session的使用
			Session 简单介绍 session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 session 保存在服务器上. Session 的工作流程 当浏览器访问服务器 ... 
- process.env.NODE_ENV理解
			1.理解NODE_ENV 在node中,有全局变量process表示的是当前的node进程.process.env包含着关于系统环境的信息.但是process.env中并不存在NODE_ENV这个东西 ... 
- 009 SpringBoot+Swagger的使用
			一:概述 1.说明 Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务. 总体目标是使客户端和文件系统作为服务器以同样的速度来更新. 文件的方法 ... 
- Linux配置crontab
			1. 添加任务(每分钟执行一次)crontab -e* * * * * /home/lings/logRotate.sh 2. 查看日志Jun 5 20:25:01 localhost CROND[8 ... 
- [Java复习] Spring Cloud - Netflix
			Spring Cloud Netflix常用组件 服务注册与发现:Eureka 服务负载均衡:Ribbon 服务声明式客户端:Feign 服务熔断:Hystrix 服务网关: Zuul Eureka: ... 
- [转]npm 遇到 write access的问题怎么办
			ubuntu下使用 npm install -g 可能会遇到这个问题. 链接地址:https://www.jianshu.com/p/31744aa44824 
- [LeetCode] 682. Baseball Game 棒球游戏
			You're now a baseball game point recorder. Given a list of strings, each string can be one of the 4 ... 
- 初识Neo4j
			Neo4j是一个世界领先的开源图形数据库. 它是由Neo技术使用Java语言完全开发的. Neo4j的优点 它很容易表示连接的数据 检索/遍历/导航更多的连接数据是非常容易和快速的 它非常容易地表示半 ... 
