SpringBoot-集成TOTP

TOTP验证码提供了一种高效且安全的身份验证方法。它不仅减少了依赖短信或其他通信方式带来的成本和延时,还通过不断变换的密码增加了破解的难度。未来,随着技术的进步和对安全性要求的提高,TOTP及其衍生技术将继续发展并被更广泛地应用。TOTP验证码是基于时间的一次性密码算法(Time-based One-Time Password algorithm)。其核心原理是使用预共享密钥和当前时间戳生成一次性的验证码。
TOTP验证码的概念及相关分析:

定义与用途:TOTP,即基于时间的一次性密码算法,是一种利用时间同步和双方预共享的密钥来生成一次性密码的方法。这种方法主要用于双因素认证(2FA),提高账户安全性。工作机制:在TOTP中,服务器和客户端都会预先共享一个密钥。当需要验证用户身份时,客户端会基于当前时间和该预共享密钥生成一个OTP(一次性密码)。只有知道正确的密钥和准确时间的用户才能生成正确的OTP,从而通过验证。安全性增强:由于每次认证都使用新的密码且仅在短时间内有效,这使得TOTP比传统的静态密码更为安全。即使攻击者截获了一次密码,也因其很快就过期而无法再次使用。性能优化:与传统的短信发送验证码相比,TOTP不需要通信费用,且响应速度更快,因为它仅依赖于时间同步而非外部通信。广泛应用:多数现代认证系统如Google Authenticator和其他多种第三方认证应用都支持TOTP,使其成为事实上的标准之一。

TOTP验证码的原理及相关分析:

密钥预共享:服务端生成并通过安全的渠道分发一个唯一的密钥给客户端。这个密钥是后续所有操作的基础。时间戳的使用:客户端根据当前时间和预共享的密钥计算一次性密码。这个过程通常每30秒进行一次,确保密码的新鲜性和安全性。HMAC-SHA1算法:使用预共享密钥和当前的时间计数作为输入,通过HMAC-SHA1算法生成一串固定长度的输出值。此输出经过特定处理后被转换为较短的数位,形成最终的验证码。服务器验证:当用户提交OTP时,服务器也会使用相同的方法和密钥计算当时的OTP应是什么,并与用户提供的值进行比较,以此判断用户的验证请求是否有效。容错机制:考虑到客户端和服务端的时钟可能不完全同步,TOTP算法允许有一定的时间容错,通常为前后几秒的时间窗口内,这保证了合法用户的正常体验。

以下是关于TOTP验证码的普及与应用以及注意事项:

普及与应用:随着移动设备的普及和互联网安全问题的增加,TOTP作为一种安全便捷的认证方式,正在被越来越多的场景所采用,包括企业级应用、金融服务、在线教育平台等。
注意事项:虽然TOTP提高了安全性,但仍应注意保护预共享密钥的安全,避免密钥泄露导致的潜在风险。同时,应定期更新密钥,防止长时间使用同一密钥增加的风险。

二维码是,下面label和issuer随意生成,secret是服务端根据每个用户生成

otpauth://totp/{label}?secret={secret}&issuer={issuer}

写一个starter

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>sf-framework</artifactId><groupId>cn.nexteer.boot</groupId><version>${revision}</version></parent><modelVersion>4.0.0</modelVersion><artifactId>sf-spring-boot-starter-otp</artifactId><packaging>jar</packaging><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>cn.nexteer.boot</groupId><artifactId>sf-common</artifactId></dependency><!-- RPC 远程调用相关 --><dependency><groupId>cn.nexteer.boot</groupId><artifactId>sf-spring-boot-starter-rpc</artifactId><optional>true</optional></dependency><!-- 业务组件 --><dependency><groupId>cn.nexteer.boot</groupId><artifactId>sf-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 --><version>${revision}</version></dependency><!-- Spring 核心 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- Web 相关 --><dependency><groupId>cn.nexteer.boot</groupId><artifactId>sf-spring-boot-starter-web</artifactId><scope>provided</scope></dependency><!-- Web 相关 --><dependency><groupId>cn.nexteer.boot</groupId><artifactId>sf-spring-boot-starter-security</artifactId></dependency><!-- DB 相关 --><dependency><groupId>cn.nexteer.boot</groupId><artifactId>sf-spring-boot-starter-mybatis</artifactId></dependency><dependency><groupId>cn.nexteer.boot</groupId><artifactId>sf-spring-boot-starter-redis</artifactId></dependency><!-- Test 测试相关 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 工具类相关 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId></dependency></dependencies>
</project>

配置类

@AutoConfiguration
public class SfOtpAutoConfiguration {@Beanpublic OtpAuthAspect otpAuthAspect(){return new OtpAuthAspect();}
}
@AutoConfiguration
@EnableFeignClients(clients = AdminUserApi.class) // 主要是引入相关的 API 服务
public class SfAdminUserRpcAutoConfiguration {
}

SPI文件

在这里插入图片描述

切面类

@Aspect
@RequiredArgsConstructor
@Slf4j
public class OtpAuthAspect {static final String OTP_CODE_HEADER = "X-OTP-CODE";static final String OPT_CODE_PARAM = "otpCode";@Resourceprivate AdminUserApi adminUserApi;@Before("@annotation(otpAuth)")public void beforePointCut(JoinPoint joinPoint, OtpAuth otpAuth) throws Throwable {LoginUser loginUser = getLoginUser();HttpServletRequest request = getRequest();String otpCode = getOtpCodeByRequest(request);if (StrUtil.isBlank(otpCode)) {log.error("[around][用户({}) 请求({}) 时,未传递 OTP 验证码]", loginUser.getId(), request.getRequestURI());throw new ServiceException(GlobalErrorCodeConstants.OTP_ERROR.getCode(), otpAuth.message());}String secret = getKeyByLoginUserId(loginUser.getId());if (StrUtil.isBlank(secret)) {log.error("[around][用户({}) 请求({}) 时,未配置 OTP 密钥]", loginUser.getId(), request.getRequestURI());throw new ServiceException(GlobalErrorCodeConstants.OTP_ERROR.getCode(), otpAuth.message());}boolean result = TotpUtils.verify(secret,otpCode);if (!result) {log.error("[around][用户({}) 请求({}) 时,OTP 验证码错误]", loginUser.getId(), request.getRequestURI());throw new ServiceException(GlobalErrorCodeConstants.OTP_ERROR.getCode(), otpAuth.message());}}private String getKeyByLoginUserId(Long id) {CommonResult<AdminUserRespDTO> user = adminUserApi.getUser(id);AdminUserRespDTO checkedData = user.getCheckedData();return checkedData.getOtpSecret();}private String getOtpCodeByRequest(HttpServletRequest request) {String header = request.getHeader(OTP_CODE_HEADER);if (StrUtil.isNotBlank(header)) {return header;}String attribute = (String)request.getAttribute(OPT_CODE_PARAM);if (StrUtil.isNotBlank(attribute)) {return attribute;}String parameter = request.getParameter(OPT_CODE_PARAM);if (StrUtil.isNotBlank(parameter)) {return parameter;}return null;}
}

工具类

@Slf4j
public class TotpUtils {private static int WINDOW_SIZE = 1;private static long X = 30;private TotpUtils() {}/*** 该方法使用JCE提供加密算法。* HMAC使用加密哈希算法作为参数计算哈希消息认证码。* @param crypto: the crypto algorithm (HmacSHA1, HmacSHA256,*                             HmacSHA512)* @param keyBytes: 用于HMAC密钥的字节* @param text: 用于HMAC密钥的字节数*/private static byte[] hmac_sha(String crypto, byte[] keyBytes,byte[] text){try {Mac hmac;hmac = Mac.getInstance(crypto);SecretKeySpec macKey =new SecretKeySpec(keyBytes, "RAW");hmac.init(macKey);return hmac.doFinal(text);} catch (GeneralSecurityException gse) {throw new UndeclaredThrowableException(gse);}}/*** This method converts a HEX string to Byte[]* @param hex: the HEX string* @return: a byte array*/private static byte[] hexStr2Bytes(String hex){// Adding one byte to get the right conversion Values starting with "0" can be convertedbyte[] bArray = new BigInteger("10" + hex,16).toByteArray();// Copy all the REAL bytes, not the "first"byte[] ret = new byte[bArray.length - 1];for (int i = 0; i < ret.length; i++)ret[i] = bArray[i+1];return ret;}private static final int[] DIGITS_POWER// 0 1  2   3    4     5      6       7        8= {1,10,100,1000,10000,100000,1000000,10000000,100000000 };/*** This method generates a TOTP value for the given* set of parameters.** @param key: the shared secret, HEX encoded* @param time: a value that reflects a time* @param returnDigits: number of digits to return** @return: a numeric String in base 10 that includes truncationDigits digits*/public static String generateTOTP(String key,String time,String returnDigits){return generateTOTP(key, time, returnDigits, "HmacSHA1");}/*** This method generates a TOTP value for the given* set of parameters.** @param key: the shared secret, HEX encoded* @param time: a value that reflects a time* @param returnDigits: number of digits to return** @return: a numeric String in base 10 that includes truncationDigits digits*/public static String generateTOTP256(String key,String time,String returnDigits){return generateTOTP(key, time, returnDigits, "HmacSHA256");}/*** This method generates a TOTP value for the given* set of parameters.** @param key: the shared secret, HEX encoded* @param time: a value that reflects a time* @param returnDigits: number of digits to return** @return: a numeric String in base 10 that includes truncationDigits digits*/public static String generateTOTP512(String key,String time,String returnDigits){return generateTOTP(key, time, returnDigits, "HmacSHA512");}/*** This method generates a TOTP value for the given* set of parameters.** @param key: the shared secret, HEX encoded* @param time: a value that reflects a time* @param returnDigits: number of digits to return* @param crypto: the crypto function to use** @return: a numeric String in base 10 that includes truncationDigits digits*/public static String generateTOTP(String key,String time,String returnDigits,String crypto){int codeDigits = Integer.decode(returnDigits).intValue();String result = null;// Using the counter// First 8 bytes are for the movingFactor// Compliant with base RFC 4226 (HOTP)while (time.length() < 16 )time = "0" + time;// Get the HEX in a Byte[]byte[] msg = hexStr2Bytes(time);byte[] k = hexStr2Bytes(key);byte[] hash = hmac_sha(crypto, k, msg);// put selected bytes into result intint offset = hash[hash.length - 1] & 0xf;int binary =((hash[offset] & 0x7f) << 24) |((hash[offset + 1] & 0xff) << 16) |((hash[offset + 2] & 0xff) << 8) |(hash[offset + 3] & 0xff);int otp = binary % DIGITS_POWER[codeDigits];result = Integer.toString(otp);while (result.length() < codeDigits) {result = "0" + result;}return result;}/*** 验证动态口令是否正确* @param secretBase32 密钥* @param code 待验证的动态口令* @return*/public static boolean verify(String secretBase32, String code){String secretHex = HexUtil.encodeHexStr(Base32Codec.Base32Decoder.DECODER.decode(secretBase32));long t = System.currentTimeMillis() / 1000L / X;for (int i = -WINDOW_SIZE; i <= WINDOW_SIZE; ++i) {String steps = Long.toHexString(t).toUpperCase();while (steps.length() < 16) steps = "0" + steps;String totp = generateTOTP(secretHex, steps, "6","HmacSHA1");if (code.equals(totp)) {return true;}}return false;}
}

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

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

相关文章

多模态模型是什么意思(国内外的AI多模态有哪些)

在人工智能和机器学习的领域&#xff0c;我们经常会遇到一些专业术语&#xff0c;这些术语可能会让初学者感到困惑。其中&#xff0c;"多模态模型"就是这样一个概念。 什么是AI多模态。它是什么意思呢&#xff1f; 那么&#xff0c;多模态模型是什么意思呢&#xff1…

【Python】数据处理:SQLite操作

使用 Python 与 SQLite 进行交互非常方便。SQLite 是一个轻量级的关系数据库&#xff0c;Python 标准库中包含一个名为 sqlite3 的模块&#xff0c;可以直接使用。 import sqlite3数据库连接和管理 连接到 SQLite 数据库。如果数据库文件不存在&#xff0c;则创建一个新数据库…

SystemVerilog Interface Class的妙用

前言 Interface Class是在SystemVerilog 2012版本中引入的&#xff0c;但目前在验证中几乎很少采用&#xff0c;大多数验证工程师要么不知道它&#xff0c;要么没有看到使用它的任何好处&#xff0c;这使得Interface Class成为一个未被充分使用和不被重视的特性。本文将举两个…

docker镜像深入理解

大家好&#xff0c;本篇文章和大家聊下docker相关的话题~~ 工作中经常有关于docker镜像的问题&#xff0c;让人百思不解 docker镜像加载到系统中到哪里去了&#xff1f;docker load 加载镜像的流程是怎样的&#xff1f;为什么容器修改内容后&#xff0c;删除容器后再次开启容…

阿里云 MQTT 服务器搭建与测试(上传和下发数据finish)

一、 MQTT 概念 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于 TCP/IP协议上,由 IBM 在 1999 年发布。MQTT 最大优点在于,可以以极少的代码和有限的带宽,…

c++之旅第十弹——IO流

大家好啊&#xff0c;这里是c之旅第十弹&#xff0c;跟随我的步伐来开始这一篇的学习吧&#xff01; 如果有知识性错误&#xff0c;欢迎各位指正&#xff01;&#xff01;一起加油&#xff01;&#xff01; 创作不易&#xff0c;希望大家多多支持哦&#xff01; 一.流的概念&…

kNN算法-概述

所谓kNN算法就是K-nearest neigbor algorithm。这是似乎是最简单的监督机器学习算法。在训练阶段&#xff0c;kNN算法存储了标签训练样本数据。简单地说&#xff0c;就是调用训练方法时传递给它的标签训练样本会被它存储起来。 kNN算法也叫lazy learning algorithm懒惰学习算法…

计算机网络 期末复习(谢希仁版本)第8章

元文件就是一种非常小的文件&#xff0c;它描述或指明其他文件的一些重要信息。这里的元文件保存了有关这个音频/视频文件的信息。 10. 流式&#xff1a;TCP&#xff1b;流式实况&#xff1a;UDP。

Huawei 大型 WLAN 组网 AC 间漫游

AC1配置命令 <AC6005>display current-configuration # vlan batch 100 # interface Vlanif100description to_S3_CAPWAPip address 10.0.100.254 255.255.255.0 # interface GigabitEthernet0/0/1port link-type trunkport trunk allow-pass vlan 100# ip route-stati…

Chrome浏览器书签同步不及时怎么办?两种方法帮你解决!

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

7种方法教你如何解决msvcp140_1.dll丢失问题,一键修复dll丢失问题

msvcp140_1.dll 是 Microsoft Visual C 2015 Redistributable 的一部分&#xff0c;它提供了运行时所需的 C 标准库的实现。这个 DLL 文件对于依赖 Visual C 2015 编译的应用程序至关重要&#xff0c;因为它包含了程序运行时所必需的函数和资源。 作用 运行时支持&#xff1a…

28-LINUX--I/O复用-epoll

一.epoll概述 epoll 是 Linux 特有的 I/O 复用函数。它在实现和使用上与 select、poll 有很大差异。首 先&#xff0c;epoll 使用一组函数来完成任务&#xff0c;而不是单个函数。其次&#xff0c;epoll 把用户关心的文件描述 符上的事件放在内核里的一个事件表中。从而无需像…

mysql (事物)

一.什么是事物 事物是一组操作的集合&#xff0c;不可分割的工作单位&#xff0c;事物会把所有的操作当作一个整体一起向系统提交或撤销操作请求&#xff0c;就是这些操作要么一起成功要么一起失败。 二.事物操作 &#xff08;这个就是一个理解&#xff09; 1.事务特性 原子性…

超详解——python数字和运算——小白篇

目录 1.位运算 2. 常用内置函数/模块 math模块&#xff1a; random模块&#xff1a; decimal模块&#xff1a; 3.内置函数&#xff1a; 总结&#xff1a; 1.位运算 位运算是对整数在内存中的二进制表示进行操作。Python支持以下常见的位运算符&#xff1a; 按位与&…

C语言王国——数据的内存管理

目录 一、引言 二、整形在内存中的存储 2.1 进制之间的转换 2.1.1 整形的二进制 2.1.2 十进制和二进制 2.1.3 十进制和八进制的转换 2.1.4 十六进制和十进制的转换 2.2 原码&#xff0c;反码&#xff0c;和补码 三、大、小端字节序 3.1 大小端的定义 3.2 为什么会有大…

pxe批量部署linux介绍

1、PXE批量部署的作用及必要性&#xff1a; 1&#xff09;智能实现操作系统的批量安装&#xff08;无人值守安装&#xff09;2&#xff09;减少管理员工作&#xff0c;提高工作效率3&#xff09;可以定制操作系统的安装流程a.标准流程定制(ks.cfg)b.自定义流程定制(ks.cfg(%pos…

LLVM Cpu0 新后端8 尾调用优化 Stack Overflow Exception异常

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1V_tZkt9uvxo5bnUufhMQ_Q?…

【iOS】JSONModel源码阅读笔记

文章目录 前言一、JSONModel使用二、JSONModel其他方法转换属性名称 三、源码分析- (instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err[self init]__setup____inspectProperties - (BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMa…

android集成百度文心一言实现对话功能,实战项目讲解,人人都能拥有一款ai应用

大家好&#xff0c;今天给大家讲解下如何实现一个基于百度文心一言的app功能&#xff0c;app内部同时集成了讯飞的语音识别。本文适用于有android基础的小伙伴阅读&#xff0c;文章末尾放上本项目用到的全部实例代码&#xff0c;在使用前请务必看完本文章。 先来给大家看看效果…

微信小程序毕业设计-医院挂号系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…