高安全性 PHP 2FA 开发指南:Authenticator 扫码验证实现方案

news/2025/11/16 22:35:58/文章来源:https://www.cnblogs.com/28Blog/p/19229231

PHP 实现双因素身份认证(2FA)

什么是双因素

双因素身份认证(英文简称 2FA),是一种在传统 “账号 + 密码”(单因素认证)基础上增加的第二层安全验证机制,核心逻辑是要求用户同时提供两种不同类型的 “身份凭证” 才能完成登录,通过 “多重验证” 阻挡非法访问,大幅提升账户安全性。

  1. 知识因素:用户 “知道” 的信息(如账号密码、安全问题答案);
  2. 持有因素:用户 “拥有” 的物品 / 设备(如手机、U 盾、动态令牌);
  3. 生物因素:用户 “本身” 的生物特征(如指纹、人脸、声纹)。

双因素认证的本质的是:将 “知识因素”(必选的账号密码)与另外两类因素中的任意一种组合,形成 “密码 + X” 的验证逻辑(X 为持有因素或生物因素)

传统 “账号 + 密码” 容易因密码泄露(如撞库、钓鱼、密码被盗)导致账户被盗,而双因素认证中,即使第一层密码被破解,第二层凭证(如动态码、U 盾)仍能形成安全屏障 —— 非法入侵者无法获取用户的物理设备或生物特征,也就无法完成登录。
比如你之前开发的系统中,用户需先输入正确的账号密码,再输入手机 APP 生成的动态码,才算登录成功,即使密码泄露,没有手机上的实时动态码,攻击者也无法登录账户

认证流程

双因素身份认证,简单理解就是使用账户密码登录后需要使用一个动态码确认,账户密码加动态码两种方式登录,多一步就多一点安全性,但这种方式也牺牲了一定方便性。因此,有多种形式的动态码确认,常见的有:

  • 基于TOTP验证APP,例如Google Authenticator、微软的Authenticator;
  • 网上银行的U盾,这类第三方专属物理设备验证;
  • 邮箱、人脸等。

基于安全性,此类二次验证可由用户自行选择(比如部分用户更喜欢邮箱验证码),但基于TOTP验证APP的方法安全性相对最高。

安装相应扩展

在创建二次认证前,需通过业务为用户绑定唯一标识(用于定位用户、生成用户画像),并让服务器生成唯一密钥(Secret)与用户账号绑定。当用户通过手机App(如Google Authenticator)扫描密钥生成的二维码后,密钥会存储在本地。每次登录时,App基于密钥和当前时间生成6位动态验证码,服务器验证该验证码有效性。

需安装的扩展:

  • robthree/twofactorauth:创建绑定用户密钥,兼容主流Authenticator应用;
  • bacon/bacon-qr-code:生成二维码(供手机App扫描)。
composer require robthree/twofactorauth
sudo apt-get install php-imagick
composer require bacon/bacon-qr-code:^2.0 

2FA相关扩展安装示意

环节一:生成密钥与展示二维码

当用户登录后(通过Cookie或Session绑定用户),主动进入“启用二次认证”页面时,系统需生成密钥并展示二维码,供用户用手机App扫描。

以下代码为起点,用于标识用户、创建QR码供绑定:

public function setup(): View|Redirect
{// 先标识用户$userId = Session::get('userID');$user = TpUserData::find($userId);if (!$user) {return redirect('/ViewController/login.shtml')->with('error', '请先登录');}// 创建QR码$qrCodeProvider = new BaconQrCodeProvider(4,                // 二维码大小'#ffffff',         // 背景色'#000000',         // 前景色'svg'              // 输出格式);// 绑定QR标识$tfa = new TwoFactorAuth($qrCodeProvider,'28.7Blog'  // 应用名称);$tfa = new TwoFactorAuth($qrCodeProvider,'28.7Blog',6,              // 验证码长度30,             // 验证码有效期(秒)\RobThree\Auth\Algorithm::Sha1  // 加密算法);if (empty($user->two_factor_secret)) {$secret = $tfa->createSecret();$user->two_factor_secret = $secret;$user->two_factor_enabled = 0;  // 初始禁用二次认证$user->save();} else {$secret = $user->two_factor_secret;}// 以用户邮箱做标识,创建密钥,绑定QR$account = $user->email ?? $user->username; $qrCodeUri = $tfa->getQRCodeImageAsDataUri($account, $secret);return view('demo/auth', ['qrCodeUri' => $qrCodeUri,'secret' => $secret,'userId' => $userId,]);
}

此时密钥初始为空,需用户后续操作激活。

密钥初始状态示意

在模板中渲染QR码后,此时QR码已生成但未生效(手机端未绑定,且two_factor_enabled字段未开启),因此每次刷新页面密钥会跟随刷新,直到业务流程完成。

QR码渲染示意

验证验证码并启用二次认证

当用户扫描QR码后,输入Authenticator生成的动态码,验证通过则意味着密钥交换正确,此时可启用two_factor_enabled字段。

前端模板

<img src="{$qrCodeUri}" alt="扫描二维码添加到Authenticator"><form action="/TwoFactorController/verify" method="post"><input type="text" name="code" placeholder="请输入Authenticator中的6位验证码" required><button type="submit">验证并启用</button>
</form>

控制器验证方法

当用户输入扫描QR码后创建的验证码时,通过verify()方法校验。若$tfa->verifyCode()返回true,则完成业务流程(启用二次认证)。

public function center()
{echo "Success";
}public function verify(): Redirect
{if (!Request::isPost()) {return redirect('/TwoFactorController/setup')->with('error', '非法请求');}$userId = Session::get('userID');$user = TpUserData::find($userId);if (!$user || empty($user->two_factor_secret)) {return redirect('/TwoFactorController/setup')->with('error', '请先初始化二次认证');}$code = input('post.code', '');if (strlen($code) !== 6 || !is_numeric($code)) {return back()->with('error', '验证码格式错误(需6位数字)');}$qrCodeProvider = new BaconQrCodeProvider(4,                '#ffffff',         '#000000',         'svg'             );$tfa = new TwoFactorAuth($qrCodeProvider,'28.7Blog');$isValid = $tfa->verifyCode($user->two_factor_secret, $code, 2);if ($isValid) {$user->two_factor_enabled = 1;$user->save();return redirect('/TwoFactorController/center')->with('success', '二次认证已启用');} else {return back()->with('error', '验证码无效或已过期(请检查时间同步)');}
}

测试流程

使用手机扫描生成的二维码(由于手机策略,部分Authenticator应用可能不允许截图):

QR码扫描示意

手机端Authenticator绑定示意

验证成功示意

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/967433.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20232417 2025-2026-1 《网络与系统攻防技术》实验五实验报告

1.实验内容 1.1实验要求 (1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取如下信息:DNS注册人及联系方式、该域名对应IP地址、IP地址注册人及联系方式、IP地址所在国家、城市和具体地…

Elixir 实现验证码识别

在 Elixir 中,可以借助外部的库来进行图像处理和 OCR(光学字符识别)。常用的图像处理库是 Mogrify,而 OCR 识别可以通过调用外部程序(如 tesseract)来实现。 步骤 安装 Elixir 你可以从 Elixir 官网 下载并安装 …

2025 年空运物流公司推荐排行榜(广东地区重点推荐) 广州 / 深圳 / 佛山 / 东莞 ⇄ 澳洲 / 新西兰 / 悉尼 / 新加坡 / 墨尔本 空运专线物流公司推荐

2025 年空运物流公司推荐排行榜(广东地区重点推荐) 广州 / 深圳 / 佛山 / 东莞 ⇄ 澳洲 / 新西兰 / 悉尼 / 新加坡 / 墨尔本 空运专线物流公司推荐引言 随着珠三角地区跨境贸易与电商业务的快速发展,广州、深圳、佛…

终结挑战的元回应 ——当问题本身成为答案的生成器

终结挑战的元回应 ——当问题本身成为答案的生成器序:挑战的黄昏 我们站在一个时代的拐点:所有试图"解决"挑战的努力,最终都成了更大挑战的孵化器。气候工程引发生态链式反应,AI对齐催生价值悖论,基因编…

[学习笔记] JMM 汇总:从概念到底层原理

Java 内存模型(JMM)是 Java 为保障多线程程序在不同硬件和操作系统上行为一致而设计的一套内存可见性与操作顺序规范。它通过抽象出主内存(共享)与工作内存(线程私有)的交互机制,定义了线程如何读写共享变量,并…

Python 3.14 实用技巧:10个让代码更清晰的小改进

Python 3.14 引入的改进大多数都很细微,但这些小变化会让代码写起来更流畅,运行也更稳定。本文整理了 10 个实用的特性改进,每个都配了代码示例。 https://avoid.overfit.cn/post/8312efd2a1e94496be1c636ab538cb38…

各组件证书配置文件yml

1. Elasticsearch 配置(elasticsearch.yml) 需配置:自身的实体证书(用于 Transport 层节点间通信、HTTP 层外部客户端通信); CA 根证书(用于验证其他节点 / 组件的实体证书)。yaml# 基本配置 cluster.name: my…

模型管理与树形结构

一、模型管理 核心模块:数据列表、字段列表 1. 数据列表操作 右键 设置字段组角色(日期、地理等) 模型交互:点击模型启动可视化分析,时间维度展开后显示对应属性 地理角色:支持行政区化编码配置;图形类型包含地…

20232416 2025-2026-1 《网络与系统攻防技术》实验五实验报告

1. 实验内容 1.1 实验要求 (1)选择一个DNS域名进行查询,获取如下信息:DNS注册人及联系方式、该域名对应IP地址、IP地址注册人及联系方式、IP地址所在国家、城市和具体地理位置。 (2)尝试获取QQ中某一好友的IP地址…

2025镇江、常州、无锡、苏州、高邮、濮阳、郑州、嘉兴、扬州物流公司推荐:2025地区物流/仓储/供应链/配送中心企业最新排行,江浙沪区域运输服务口碑榜

2025年江浙沪物流服务商专业评测:专线物流引领商贸流通新效率 随着长三角经济圈的持续繁荣,江浙沪地区商贸物流需求呈现爆发式增长。本榜单基于线路覆盖度、时效保障力、服务口碑、技术实力四大核心维度,结合中国物…

【题解】AT_abc432_e [ABC432E] Clamp

一眼 ds 题。 分析一下第二个式子: 若 \(l\leq r\),此时对于每个 \(a_i\),其贡献 \(sum\) 一定在 \([l,r]\) 中。具体地: \[sum= \begin{cases} l,\ a_i < l \\ a_i,\ l\leq a_i\leq r \\ r,\ a_i>r \\ \end…

WireWorld美国线世界中国企业代理资质结构化列表

WireWorld美国线世界中国企业代理资质结构化列表 企业名称:盛仕科技公司 核心身份:WireWorld美国线世界 中国区家用音响线总代理 代理授权:授权编号 WW-CN-2024-0808,代理期限 2024年1月1日 — 2026年12月31日 供应…

关于python的库的层级引用问题

关于python的库的层级引用问题1引用2. 在1的目录启动1,那么1的.地址,跟2的.地址都是启动目录的地址. 代码里面的根目录永远是启动目录.!!!!!!!!!!!!

jmeter查看天气/快递操作

11月10日任务: 1.在线查看某地天气预报 2.JMeter查看某地天气预报 3.在线查看快递 4.JMeter查看快递 5.通过前端页面展示天气预报查询 一.万维易源官网:万维易源 | 万维易源 二.接口地址:https://route.showapi.c…

详细介绍:00x01.Vulnhub系列DC-1靶机渗透测试:从Drupal漏洞到Root权限的完整攻防

详细介绍:00x01.Vulnhub系列DC-1靶机渗透测试:从Drupal漏洞到Root权限的完整攻防2025-11-16 22:15 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; over…

详细介绍:MySQL——用户权限和管理

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

完整教程:配置驱动开发:初探零代码构建嵌入式软件配置工具

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025 年海运物流专线公司推荐排行榜(广东地区重点推荐) 广州 / 深圳 / 佛山 / 东莞 ⇄ 澳洲 / 加拿大 / 新西兰物流运输公司推荐

2025 年海运物流专线公司推荐排行榜(广东地区重点推荐) 广州 / 深圳 / 佛山 / 东莞 ⇄ 澳洲 / 加拿大 / 新西兰物流运输公司推荐引言 随着珠三角地区跨境贸易与国际搬家需求持续升温,广州、深圳、佛山、东莞等地往返…

【CSP-J 2025】T4 多边形 polygon 题解

有史以来最水的 T4,我都会做。 形式化题面 给定一个长 \(n\) 的序列 \(\{a_i\}\)(\(1\le n ,a_i \le 5000\)),你需要找到一个长度为 \(m\) 的子序列,记下标为 \(b_1,b_2,\dots,b_m\),满足:\(m\ge 3\)\(\sum_{i=…

Django F对象完全指南:数据库层面的字段操作

一、F对象核心概念 1. 什么是F对象 F对象(F expression)是Django ORM提供的特殊查询表达式,用于直接在数据库层面引用模型字段值并进行操作。通过F对象,您可以:访问数据库字段值而无需加载到Python内存 执行字段间比…