news 2026/6/24 9:17:22

Node.js躬行记(32)——F2A实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Node.js躬行记(32)——F2A实战

F2A(Two-Factor Authentication)其实是2FA的笔误,都表示双因素认证。它是一种安全验证机制,要求用户在登录时提供两种不同类型的证据(因素)来证明身份。

在实际应用中,F2A/2FA 最常见的表现形式是:

  1. 密码 + 短信验证码:最普遍但安全性相对较低(易受 SIM 卡交换攻击)。
  2. 密码 + 基于时间的一次性密码( the Time-Based One-time Password ,TOTP):如 Google Authenticator、Microsoft Authenticator 等应用生成的 6-8 位动态数字。
  3. 密码 + 硬件密钥:如 YubiKey,安全性最高,能有效防御网络钓鱼。

我们的后台将会采用第二种表现形式,后台服务端基于 Node.js 实现。核心实现原理是基于时间同步的一次性密码(TOTP,Time-based One-Time Password)算法,通过“共享密钥”和“时间”两个要素,在用户设备与服务器之间生成相同的动态验证码。即服务器和你的手机APP,各自使用同一个密钥和当前时间,通过相同的算法计算,得出一串相同的数字。

一、生成密钥

1)speakeasy.js

Node的生态提供了speakeasy.js库,可以生成F2A的密钥,还有供F2A设备扫描的地址。

npm install --save speakeasy

在项目中安装完成后,就是调用 speakeasy 的生成方法。

router.get( '/get/f2a', async (ctx) => { const secret = speakeasy.generateSecret({ length: 20 }); ctx.body = { code: 0, data: { secret: secret.base32, url: secret.otpauth_url, } }; }, );

secret 就是一串字符,而 url 是个 otpauth 协议的地址,生成二维码后便于设备扫描。

{ "secret": "M4STOUBFGM4UUXJXKZJXS5J4IIWEA3KU", "url": "otpauth://totp/SecretKey?secret=M4STOUBFGM4UUXJXKZJXS5J4IIWEA3KU" }

2)账户绑定密钥

点击上图中的重新绑定按钮,就能将当前 secret 和后台账户绑定起来,在账户表中新增两个字段:otpauthUrl 和 secret。

{ "_id": { "$oid": "5f81288d3578bb005a79cdc1" }, "status": 1, "realName": "测试", "userName": "xx@xx.me", "cellphone": "13800138000", "password": "abcd", "otpauthUrl": "otpauth://totp/SecretKey?secret=PU4WG5RDJVZVCSDHMM5UYSBFEMSEANBQ", "secret": "PU4WG5RDJVZVCSDHMM5UYSBFEMSEANBQ" }

后续在登录输入安全码后,就可进行校验了。

二、绑定应用

1)APP

在应用市场提供了很多的APP,用于显示安全码,界面基本上都是下图这样,例如 authy、2FAS 等。

2)小程序

有个叫二次验证码的小程序,也能用于绑定。

3)飞书小助手

还设计了一种更简便的查询方式。

在机器人的对话框,输入安全码,空格后面跟上自己管理后台的账号邮箱,例如“安全码 xx@xx.com”。

如此,就能得到该账户的最新安全码。

原理就是查表,然后调用 speakeasy 的 totp() 方法,再调用飞书的接口发送消息。

const account = await this.models.BackendUserAccount.findOne({ userName: email }); const token = speakeasy.totp({ secret: account.secret, encoding: 'base32', }); // 发送回复消息 await this.messageService.sendTextMessage(chat_id, token);

不过还有个小问题,就是也能收到别人的安全码。但后台都有查询记录,若出现问题,还能溯源。

三、F2A校验

1)开关

增加一步登录校验,在使用时会增加门槛,所以在通用配置中设计了一个开关。

{ "isOpen": false, "whiteList": [ "yy@yy.com" ] }

只有在 isOpen 为 true 时,才会开启校验。

whiteList 是给特殊账户开启的白名单,不需要走校验,例如给第三方用的账户。

2)登录

登录设计成了两步,两次调用的接口都是 user/login。

第一步还是原先的输入邮箱和密码。

输入完后,去请求登录接口,判断是否需要二次校验,若返回JSON包含 f2a。

{ f2a: true }

则显示弹框,安全码为必填项,点击确定进行二次校验。

下面是登录接口中的部分逻辑,从通用配置中读取开关信息,判断是否弹框,最后校验。

// 判断是否需要F2A安全校验 const configContent = await services.tool.getConfigContent({ key: '1a26f4185f66d6f94ef3897f7a475305' }); if(configContent && configContent.isOpen && configContent.whiteList.indexOf(userName) === -1) { // 未传随机码,说明是第一步校验,直接返回 if(!code) { ctx.body = { f2a: true }; return; } // 校验随机码 const verifyInfo = { secret: account.secret, encoding: 'base32', token: code, } const verify = speakeasy.totp.verify(verifyInfo); if(!verify) { ctx.status = 400; ctx.body = { error: '安全码错误' }; return; } }

3)过渡期

在开启这个功能前,会有一段时间的过渡期,在登录页面增加说明文档。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/24 9:16:17

GSD:让AI编程从灵感闪现到稳定交付的智能伙伴

GSD:让AI编程从灵感闪现到稳定交付的智能伙伴 【免费下载链接】get-shit-done A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TCHES. 项目地址: https://gitcode.com/GitHub_Trending…

作者头像 李华
网站建设 2026/6/24 9:15:11

OpenLayers 是一个专为开发 WebGIS(Web 地理信息系统

OpenLayers 是一个专为开发 WebGIS(Web 地理信息系统)客户端应用而设计的强大、高性能且完全开源的 JavaScript 库。它允许开发者在网页上轻松集成丰富的交互式地图,支持从多种来源加载地图瓦片、矢量数据和标记。核心技术特点:多…

作者头像 李华
网站建设 2026/6/24 9:12:39

射频内透热 vs 红外 vs EMS vs 艾灸:四种减重设备技术路线一文说清

减重市场热了之后,市面上出现了各种各样的设备。红外热疗舱、射灸舱/热疗舱、EMS肌肉刺激仪、中频理疗仪、传统艾灸仪——名字不同,原理也完全不同。 很多采购者去看设备时,容易陷入一个误区:只看价格和外观,不看底层技…

作者头像 李华
网站建设 2026/6/24 9:09:21

上海普陀区老房翻新装修报价清单透明的公司

一、行业痛点分析在当前老房翻新领域,存在诸多技术挑战。首先,老房的结构和设施往往较为陈旧,需要进行全面的检测和评估,以确保翻新工程的安全性和可行性。其次,老房的空间布局可能不合理,需要进行优化和改…

作者头像 李华
网站建设 2026/6/24 9:02:40

IT运维智能体自主诊断与修复平台建设探讨

正处于被市场广泛接受和深度应用的关键期。然而,市场的热情与企业实际落地之间仍然存在着一道不小的鸿沟。很多企业投入重金部署了完整的监控体系——Prometheus、Zabbix、ELK、SkyWalking样样齐全,大屏美轮美奂,告警规则动辄上百条。可复盘灾…

作者头像 李华
网站建设 2026/6/24 9:01:40

LeetCode 73. 矩阵置零,从标记数组到 O(1) 空间优化彻底讲透

LeetCode 73. 矩阵置零,从标记数组到 O(1) 空间优化彻底讲透 一、题目描述 给定一个 m n 的矩阵,如果一个元素为 0,则将其所在行和所在列的所有元素都设为 0。 要求: 原地修改矩阵尽量减少额外空间使用 示例: 输…

作者头像 李华