Hutool 秒速实现 2FA 两步验证

news2025/2/12 3:32:29

前言

随着网络安全威胁的日益复杂,传统的用户名和密码认证方式已不足以提供足够的安全保障。为了增强用户账户的安全性,越来越多的应用和服务开始采用多因素认证(MFA)。基于时间的一次性密码(TOTP, Time-based One-Time Password)是多因素认证的一种流行实现方式,它通过生成随时间变化的一次性密码来提供额外的安全层。TOTP 算法由 RFC 6238 定义,广泛应用于各种安全应用中,如 Google Authenticator。

本文展示如何使用 Hutool 工具包实现 TOTP 功能,并结合 Google 的二维码生成工具 来简化密钥分发和用户配置过程。Hutool 是一个功能丰富的 Java 工具类库,提供了大量实用的功能,使得开发者能够更高效地开发应用程序。特别是其内置的支持 TOTP 和 QR Code 生成的功能,为实现多因素认证提供了极大的便利。

引入Hutool的依赖

Hutool 工具包

<!-- Hutool 工具包 -->
<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.8.22</version>
</dependency>

Google 验证码工具包

<!-- Google 二维码 -->
<dependency>
	<groupId>com.google.zxing</groupId>
	<artifactId>core</artifactId>
	<version>3.5.3</version>
</dependency>

说明:
Hutool 可以按需引入依赖的,这里我没有太过于讲究,直接引入 all 所有包,另外生成 TOTP 的一次性密码需要生成二维码,而 Hutool 刚好就支持Google 的zxing二维码工具包,只不过使用 Hutool 的二维码工具包需要用户手动引入Zxing 包来完成支持

实现代码

package com.hsqyz.web.utils;

import cn.hutool.crypto.digest.otp.TOTP;
import cn.hutool.core.codec.Base32;
import cn.hutool.extra.qrcode.QrCodeUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.time.Instant;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * TOTP 测试类
 */
@Slf4j
public class TOTP_Test {

    public static void main(String[] args) throws InterruptedException {
        // 用户账户名
        String account = "花伤情犹在"; // 用户账户名

        // 指定密钥长度为 15 字节(120 位)
        int numBytes = 15;

        // 生成 QR Code URL
        String qrCodeUrl = TOTP.generateGoogleSecretKey(account, numBytes);
        System.out.println("QR Code URL for Google Authenticator: " + qrCodeUrl);

        // 提取密钥并打印
        String secretKey = extractSecretFromUri(qrCodeUrl);
        System.out.println("提取的密钥是: " + secretKey);

        // 创建 TOTP 对象,默认时间步长为30秒
        byte[] keyBytes = Base32.decode(secretKey); // 将 Base32 编码的字符串转换为字节数组
        TOTP totp = new TOTP(keyBytes);

        // 生成二维码图片并保存为文件
        QrCodeUtil.generate(qrCodeUrl, 200, 200, new File("totp_qrcode.png"));
        System.out.println("二维码已保存为 'totp_qrcode.png'");

        // 开始无限循环,持续打印最新的一次性密码
        while (true) {
            // 获取当前的一次性密码
            int otpCode = totp.generate(Instant.now());
            System.out.println("当前的一次性密码是: " + String.format("%06d", otpCode));

            // 等待 1 秒,以匹配 TOTP 的时间窗口
            Thread.sleep(1000 * 1);
        }
    }

    /**
     * 使用正则表达式从 URI 中提取密钥。
     *
     * @param uri 要解析的 URI 字符串
     * @return 提取的密钥字符串
     */
    private static String extractSecretFromUri(String uri) {
        // 定义正则表达式
        String regex = "secret=([^&]+)";

        // 编译正则表达式
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(uri);

        // 查找并提取密钥
        if (matcher.find()) {
            return matcher.group(1); // 返回第一个捕获组的内容
        } else {
            throw new IllegalArgumentException("URI 中没有找到 secret 参数");
        }
    }

}

运行效果:

使用 Google Authenticator 扫描加入查看:

为什么要使用Base32 进行密钥解码

Base32解码

使用 Base32 解码的原因在于 TOTP(基于时间的一次性密码)算法的工作方式。为了确保生成的一次性密码是安全且可验证的,TOTP 使用共享密钥(secret key)作为输入之一。这个共享密钥通常是通过 Base32 编码进行表示和传输的

Base32 编码的字符集

Base32 编码使用的是一个特定的字符集,包括:

  • 大写字母:A-Z
  • 数字:2-7
  • 填充字符(可选):=

完整字符集

Base32 的完整字符集是:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7

如果密钥包含特殊字符会怎么样?

如果密钥中包含特殊字符并且尝试使用 Base32 编码,可能会导致一系列问题。Base32
编码有其特定的字符集和格式要求,如果密钥中包含不属于 Base32 字符集的字符,编码和解码过程可能会失败或产生不正确的结果。

  1. 编码失败:
    如果密钥中包含不属于上述字符集的字符(例如 @, !, # 等),在进行 Base32 编码时,编码器可能会抛出异常或生成无效的编码字符串。
  2. 解码失败:
    即使编码成功,但生成的 Base32 字符串中包含了非法字符,在解码时也会失败,因为这些字符无法被正确解析为原始字节数组。
  3. 数据损坏:
    如果某些特殊字符被错误地处理或替换,可能会导致最终解码出来的字节数组与原始密钥不一致,从而影响 TOTP 一次性密码的生成和验证。

解决方法
为了避免这些问题,建议确保密钥只包含合法的 Base32 字符。如果您需要生成一个新的密钥,可以使用专门的工具或库来生成符合 Base32 格式的随机密钥。

示例:生成合法的 Base32 密钥

import cn.hutool.core.codec.Base32;
import cn.hutool.crypto.digest.DigestUtil;

public class GenerateValidBase32Key {
    public static void main(String[] args) {
        // 生成随机字节数组(例如 15 字节)
        byte[] randomBytes = DigestUtil.randomBytes(15);

        // 将字节数组编码为 Base32 字符串
        String base32Key = Base32.encode(randomBytes);
        System.out.println("生成的合法 Base32 密钥是: " + base32Key);
    }
}

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

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

相关文章

【继承】—— 我与C++的不解之缘(十九)

前言&#xff1a; 面向对象编程语言的三大特性&#xff1a;封装、继承和多态 本篇博客来学习C中的继承&#xff0c;加油&#xff01; 一、什么是继承&#xff1f; ​ 继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段&#xff0c;它允许我们在保持原有类…

【目标跟踪】Anti-UAV数据集详细介绍

Anti-UAV数据集是在2021年公开的专用于无人机跟踪的数据集&#xff0c;该数据集采用RGB-T图像对的形式来克服单个类型视频的缺点&#xff0c;包含了318个视频对&#xff0c;并提出了相应的评估标准&#xff08;the state accurancy, SA)。 文章链接&#xff1a;https://arxiv.…

偏差-方差权衡(Bias–Variance Tradeoff):理解监督学习中的核心问题

偏差-方差权衡&#xff08;Bias–Variance Tradeoff&#xff09;&#xff1a;理解监督学习中的核心问题 在机器学习中&#xff0c;我们希望构建一个能够在训练数据上表现良好&#xff0c;同时对未见数据也具有强大泛化能力的模型。然而&#xff0c;模型的误差&#xff08;尤其…

Figma入门-原型交互

Figma入门-原型交互 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c;对…

Windows系统怎么把日历添加在桌面上用来记事?

在众多电脑操作系统中&#xff0c;Windows系统以其广泛的用户基础和强大的功能&#xff0c;成为许多人的首选。对于习惯于在电脑前工作和学习的用户来说&#xff0c;能够直接在桌面上查看和记录日历事项&#xff0c;无疑会大大提高工作效率和生活便利性。今天&#xff0c;就为大…

蓝桥杯备赛笔记(一)

这里的笔记是关于蓝桥杯关键知识点的记录&#xff0c;有别于基础语法&#xff0c;很多内容只要求会用就行&#xff0c;无需深入掌握。 文章目录 前言一、编程基础1.1 C基础格式和版本选择1.2 输入输出cin和cout&#xff1a; 1.3 string以下是字符串的一些简介&#xff1a;字符串…

大数据新视界 -- 大数据大厂之 Hive 数据压缩:优化存储与传输的关键(上)(19/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

RNN And CNN通识

CNN And RNN RNN And CNN通识一、卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;1. 诞生背景2. 核心思想和原理&#xff08;1&#xff09;基本结构&#xff1a;&#xff08;2&#xff09;核心公式&#xff1a;&#xff08;3&#xff09;关…

求整数的和与均值

求整数的和与均值 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 读入n&#xff08;1 < n < 10000&#xff09;个整数&#xff0c;求它们的和与均值。 输入 输入第一行是一个整数n&#xff0c;…

配置idea环境进行scala编程

这里用的jdk是jdk-8u161,scala版本是2.12.0 在d盘新建一个本地仓库用来存放下载的maven包&#xff0c;在里面创建如下两个文件 更改settings文件为下面的样子 点击左下角的设置&#xff0c;更改maven本地仓库的位置&#xff08;默认在c盘用户目录下的.m2文件中&#xff0c;更改…

WSL简介与安装流程(Windows 下的 Linux 子系统)

目录 1.wsl安装 1.1 WSL简介 1.1.1 WSL 的主要功能 1.1.2 WSL 的版本 1.1.3 为什么使用 WSL&#xff1f; 1.1.4 WSL 的工作原理 1.1.5 WSL 的常见使用场景 1.1.6 与虚拟机的区别 1.1.7 适合使用 WSL 的人群 1.2 启用 WSL 1.2.1 打开 PowerShell&#xff08;管理员模…

【Java树】二叉树遍历的简单实现

二叉树的遍历 二叉树的遍历是值按照一定顺序访问二叉树中所有结点的过程&#xff0c;确保每个结点被访问且仅被访问一次。遍历操作是对二叉树的基础操作&#xff0c;用于后续的查找、排序和路径计算等功能。 二叉树的遍历有以下几种常见方式&#xff1a;深度遍历&#xff08;…

STL算法之set相关算法

STL一共提供了四种与set(集合)相关的算法&#xff0c;分别是并集(union)、交集(intersection)、差集(difference)、对称差集(symmetric difference)。 目录 set_union set_itersection set_difference set_symmetric_difference 所谓set&#xff0c;可细分为数学上定义的和…

鸿蒙ArkUI-X已更新适配API13啦

ArkUI-X 5.0.1 Release版配套OpenHarmony 5.0.1 Rlease&#xff0c;API 13&#xff0c;新增适配部分API 13接口支持跨平台&#xff1b;框架能力进一步完善&#xff0c;支持Android应用非压缩模式&#xff0c;支持Android Fragment对接跨平台。ACE Tools工具易用性提升&#xff…

rest-assured multiPart上传中文名称文件,文件名乱码

rest-assured是一个基于java语言的REST API测试框架&#xff0c;在使用rest-assured的multipart 上传文件后&#xff0c;后端获取的文件名称乱码。截图如下&#xff1a; 原因是rest-assured multipart/form-data默认的编码格式是US-ASCII&#xff0c;需要设置为UTF-8。 Befo…

前端页面或弹窗在线预览文件的N种方式

需求&#xff1a;后端返回给前端一个地址后&#xff0c;在前端页面上或则在弹框中显示在线的文档、表格、图片、pdf、video等等&#xff0c;嵌入到前端页面 方式一&#xff1a; 使用vue-office 地址&#xff1a;vue-office简介 | vue-office 个人感觉这个插件是最好用的&#x…

<<WTF-Solidity>>学习笔记(part 21-24)

part 21: 调用已部署合约 在Solidity中&#xff0c;一个合约可以调用另一个合约的函数&#xff0c;这在构建复杂的DApps时非常有用。本教程将会介绍如何在已知合约代码&#xff08;或接口&#xff09;和地址的情况下&#xff0c;调用已部署的合约。 part 22: Call call 是…

element的el-table表格标题用css自定义是否必填,用添加伪类的方式标红色*

element的el-table表格标题用css自定义是否必填添加伪类红色 * 效果图如下&#x1f447; el-table组件的html部分 css部分 /deep/.el-table__header-wrapper{.el-table__header{.has-gutter tr .el-table__cell:nth-of-type(3) .cell:before{content: *;color:red}.has-gutte…

2024 ccpc 辽宁省赛 E(构造 思维?)L(二分+一点点数论知识?)

E 题意&#xff1a; 可以注意到&#xff1a; 我的两种方格都四个方格的大小。 所以 如果存在一种摆放方式 那么 4|nm。 再考虑一种特殊的情况 22 &#xff0c;此时虽然我的积是4 但是无法摆放的。 1>对于 4 | n,或者 4 | m.我直接摆放第二种方格就可以了。 如果我n 是4 的…

【python】OpenCV—Tracking(10.5)—dlib

文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、涉及到的库函数dlib.correlation_tracker() 6、参考 1、功能描述 基于 dlib 库&#xff0c;实现指定类别的目标检测和单目标跟踪 2、代码实现 caffe 模型 https://github.com/MediosZ/MobileNet-SSD/tree/master/…