2FA-双因素认证
双因素认证(2FA,Two-Factor Authentication)是一种提高安全性的方法,要求用户在登录或进行某些敏感操作时提供两种不同类型的身份验证信息。这种方法通过引入第二层验证,增加了账户被未经授权访问的难度。
项目结构
spring-boot-2fa-demo
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── demo
│ │ │ ├── DemoApplication.java
│ │ │ ├── security
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ ├── TotpAuthenticationFilter.java
│ │ │ │ ├── TotpAuthenticationProvider.java
│ │ │ │ ├── TotpAuthenticationToken.java
│ │ │ │ └── TotpAuthenticator.java
│ │ │ └── web
│ │ │ ├── TotpSetupController.java
│ │ │ └── TotpVerifyController.java
│ └── main
│ └── resources
│ └── application.properties
└── pom.xml
1. pom.xml
<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"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-boot-2fa-demo</name><description>Spring Boot 2FA Demo</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot Starter Security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- TOTP Library --><dependency><groupId>de.taimos</groupId><artifactId>totp</artifactId><version>1.0.0</version></dependency><!-- Spring Boot Starter Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
2. DemoApplication.java
package com.example.demo; import com.example.demo.demo.security.TotpAuthenticator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext; @SpringBootApplication
public class DemoApplication { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(DemoApplication.class, args); String[] beanNames = context.getBeanNamesForType(TotpAuthenticator.class); for (String beanName : beanNames) { System.out.println("Found bean: " + beanName); }}
}```### 3. Security 配置#### `SecurityConfig.java````java
package com.example.demo.demo.security; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 配置不需要认证的路径 .antMatchers("/login", "/totp-setup", "/totp-verify", "/auth/*","/test/*").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/totp-verify") .permitAll() .and() // 在用户名密码过滤器之前添加 TOTP 认证过滤器 .addFilterBefore(new TotpAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); }
}
TotpAuthenticationFilter.java
package com.example.demo.demo.security; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; public class TotpAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public TotpAuthenticationFilter() { super(new AntPathRequestMatcher("/totp-verify")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String totp = request.getParameter("totp"); String username = request.getParameter("username"); // 创建 TOTP 认证令牌 TotpAuthenticationToken token = new TotpAuthenticationToken(username, totp); return this.getAuthenticationManager().authenticate(token); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { SecurityContextHolder.getContext().setAuthentication(authResult); chain.doFilter(request, response); }
}```#### `TotpAuthenticationProvider.java````java
package com.example.demo.demo.security; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService; public class TotpAuthenticationProvider implements AuthenticationProvider { @Autowired private TotpAuthenticator totpAuthenticator; @Autowired private UserDetailsService userDetailsService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String totp = (String) authentication.getCredentials(); // 验证 TOTP if (totpAuthenticator.verifyTotp(username, Integer.parseInt(totp))) { return new TotpAuthenticationToken(username, totp, userDetailsService.loadUserByUsername(username).getAuthorities()); } return null; } @Override public boolean supports(Class<?> authentication) { return TotpAuthenticationToken.class.isAssignableFrom(authentication); }
}
TotpAuthenticationToken.java
package com.example.demo.demo.security; import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority; import java.util.Collection; public class TotpAuthenticationToken extends AbstractAuthenticationToken { private final Object principal; private Object credentials; public TotpAuthenticationToken(Object principal, Object credentials) { super(null); this.principal = principal; this.credentials = credentials; setAuthenticated(false); } public TotpAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; this.credentials = credentials; setAuthenticated(true); } @Override public Object getCredentials() { return this.credentials; } @Override public Object getPrincipal() { return this.principal; } @Override public void eraseCredentials() { super.eraseCredentials(); credentials = null; }
}
TotpAuthenticator.java
package com.example.demo.demo.security; import com.warrenstrange.googleauth.GoogleAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;
import org.springframework.stereotype.Component; /** * @author lei */@Component
public class TotpAuthenticator { private final GoogleAuthenticator gAuth = new GoogleAuthenticator(); // 生成 TOTP 密钥并返回 GoogleAuthenticatorKey 对象 public GoogleAuthenticatorKey generateSecret() { return gAuth.createCredentials(); } // 获取 TOTP QR 码 URL public String getQRCode(GoogleAuthenticatorKey secret, String account) { return GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL(account, "SpringBootDemo", secret); } // 验证 TOTP public boolean verifyTotp(String secret, int verificationCode) { return gAuth.authorize(secret, verificationCode); }
}
4. 控制器
TotpSetupController.java
package com.example.demo.demo.web; import com.example.demo.demo.dto.QRCodeResponse;
import com.example.demo.demo.security.TotpAuthenticator;
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
import org.springframework.web.bind.annotation.*; import java.util.HashMap;
import java.util.Map; @RestController
@RequestMapping("/auth")
public class TotpSetupController { private final TotpAuthenticator totpAuthenticator; public TotpSetupController(TotpAuthenticator totpAuthenticator) { this.totpAuthenticator = totpAuthenticator; } // 设置 TOTP 密钥并返回 QR 码 URL @GetMapping("/totp-setup") public Map<String, String> setupTotp(@RequestParam String username) { // 写死一个 TOTP 密钥 String hardCodedSecret = "OZSNQGV44RGY63BL"; GoogleAuthenticatorKey googleAuthenticatorKey = new GoogleAuthenticatorKey.Builder(hardCodedSecret).build(); String qrCodeUrl = totpAuthenticator.getQRCode(googleAuthenticatorKey, username); Map<String, String> response = new HashMap<>(); response.put("secret", hardCodedSecret); response.put("qrCodeUrl", qrCodeUrl); return response; } // 设置 TOTP 密钥并返回 QR 码 URL @GetMapping("/totp-setup1") public QRCodeResponse setupTotp1(@RequestParam String username) { GoogleAuthenticatorKey googleAuthenticatorKey = totpAuthenticator.generateSecret(); // 保存密钥与用户名的关联关系,可以使用数据库等存储 // 这里只是示例,没有实际存储 String qrCodeUrl = totpAuthenticator.getQRCode(googleAuthenticatorKey, username); return new QRCodeResponse(googleAuthenticatorKey.getKey(), qrCodeUrl); }
}
TotpVerifyController.java
package com.example.demo.demo.web; import com.example.demo.demo.security.TotpAuthenticator;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*; @RestController
@RequestMapping("/test")
public class TotpVerifyController { private final TotpAuthenticator totpAuthenticator; public TotpVerifyController(TotpAuthenticator totpAuthenticator) { this.totpAuthenticator = totpAuthenticator; } @GetMapping("/totp-verify") public String verifyTotp(@RequestParam int totp) { String username = SecurityContextHolder.getContext().getAuthentication().getName(); // 从存储中获取与用户名关联的密钥,这里假设已获取 String secret = "OZSNQGV44RGY63BL"; if (totpAuthenticator.verifyTotp(secret, totp)) { return "2FA 成功!"; } else { return "无效的 TOTP!"; } } @GetMapping("/test1") public String test() { return "hell1"; }}
5. 配置文件
application.properties
server.port=8080
spring.application.name=2FA-Demo
6. 启动项目
确保所有代码都已编写完成,然后运行 DemoApplication.java
启动项目。你可以通过以下步骤测试 2FA 功能:
- 访问
/totp-setup
端点生成 TOTP 密钥和 QR 码。 - 使用 Google Authenticator 扫描 QR 码。
- 访问
/totp-verify
端点并输入 Google Authenticator 生成的一次性密码。
- 接口输出url可通过二下面工具生成
- 二维码工具:https://www.runoob.com/try/try.php?filename=tryhtml5_QRCode
相关文章:
2FA-双因素认证
双因素认证(2FA,Two-Factor Authentication)是一种提高安全性的方法,要求用户在登录或进行某些敏感操作时提供两种不同类型的身份验证信息。这种方法通过引入第二层验证,增加了账户被未经授权访问的难度。 项目结构 …...
解决 Python 中的 TypeError 错误
解决 Python 中的 TypeError 错误 在 Python 编程中,TypeError 是一种常见的错误,通常发生在尝试对不兼容的类型进行操作时。了解这个错误的原因以及如何有效解决它,对于提高代码的可靠性和可读性至关重要。本文将详细讨论 TypeError 的成因…...
快速学会C 语言基本概念和语法结构
😀前言 本篇博文是关于C 语言的基本概念和语法结构,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力&a…...
Python酷库之旅-第三方库Pandas(172)
目录 一、用法精讲 791、pandas.UInt8Dtype类 791-1、语法 791-2、参数 791-3、功能 791-4、返回值 791-5、说明 791-6、用法 791-6-1、数据准备 791-6-2、代码示例 791-6-3、结果输出 792、pandas.UInt16Dtype类 792-1、语法 792-2、参数 792-3、功能 792-4、…...
Linux系统下minio设置SSL证书进行HTTPS远程连接访问
文章目录 1.配置SSL证书使用HTTPS访问2.MINIO SDK 忽略证书验证3.使用受信任的证书 1.配置SSL证书使用HTTPS访问 生成域名对应的SSL证书,下载Apache版本,我目前只发现Apache这个里面有对应的私钥和证书 私钥重命名为private.key证书重命名为public.crt&…...
npm 包的命名空间介绍,以及@typescript-eslint/typescript-eslint
npm 包的命名空间是一个重要的概念,用于组织和管理相关的包。通过命名空间,开发者可以避免命名冲突、增强包的可读性和可维护性。以下是关于 npm 命名空间的详细介绍,并以 typescript-eslint 作为示例。 1. 命名空间的结构 命名空间的格式为…...
ecovadis评估是什么,有什么提成自己评分等级
EcoVadis评估是一个企业社会责任(CSR)评级平台,旨在评估全球供应链的可持续性和道德情况。以下是对EcoVadis评估的详细介绍以及提升其评分等级的方法: 一、EcoVadis评估概述 定义:EcoVadis评估通过一系列框架评估公司…...
Vue3中ref、toRef和toRefs之间有什么区别?
前言 Vue 3 引入了组合式 API,其中 ref、toRef 和 toRefs 是处理响应式数据的核心工具。作为高级计算机工程师,我们有必要深入理解这些工具的细微差别,以便在实际项目中更加高效地管理状态。本文将详细解析 ref、toRef 和 toRefs 的区别&…...
react开发技巧
/* eslint-disable no-useless-escape */ const Validator { isEmail: /^([a-zA-Z0-9_\.\-])\(([a-zA-Z0-9\-])\.)([a-zA-Z0-9]{2,4})$/, // 校验邮箱 isPhoneNumber: /^1[3456789]\d{9}$/, // 手机号码验证 isMobileNumber: /^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/, //…...
HarmonyOS第一课——HarmonyOS介绍
HarmonyOS第一课 HarmonyOS介绍 HarmonyOS是新一代的智能终端操作系统(泛终端服务的载体); 智慧互联协同,全场景交互体验; 核心技术理念: 一次开发 多次部署: 预览 可视化开发UI适配 事件交…...
XCode16中c++头文件找不到解决办法
XCode16中新建Framework,写完自己的c代码后,提示“<string> file not found”等诸如此类找不到c头文件的错误。 工程结构如下: App是测试应用,BoostMath是Framework。基本结构可以参考官方demo:Mix Swift and …...
CSS - 保姆级面试基础扫盲版本一
盒子模型 盒子模型定义: 当对一个盒子模型进行文档布局的时候,浏览器的渲染引擎会根据标准之一的CSS盒子模型(CSS basic box model),将所有元素表示成一个个矩阵盒子。 一个盒子通常由四部分组成:border p…...
51c自动驾驶~合集2
我自己的原文哦~ https://blog.51cto.com/whaosoft/11491137 #BEVWorld BEV潜在空间构建多模态世界模型,全面理解自动驾驶~一、引言 世界模型建模了有关环境的知识,其可以通过给定的条件对未来进行合理的想象。未来想象要求世界模型具有物理规律的理解…...
Redis后台任务有哪些
Redis后台任务 为了有更好的性能表现,redis对于一些比较耗时的操作会异步执行,不阻塞线上请求。文章从源码(redis7.0)来看,aof、rdb文件的关闭,aof文件的刷盘以及部分内存释放会采用异步方式,在后台线程中执行。接下来…...
TPair<TKey, TValue> 键值对
在 Delphi(或更准确地说是 Object Pascal,Delphi 的编程语言)中,TList<T> 是泛型列表的一个实现,其中 T 是列表中元素的类型。TPair<TKey, TValue> 是一个包含两个元素的记录(record࿰…...
【杂谈】城市规划教育的危与机
城市规划教育的危与机 (赵燕菁 原文为作者在 第21届中国城市规划学科发展论坛上的发言,有删减和改动)如有侵权,立即删除 过去几年,尤其是从2022年后房地产市场的下行开始,中国的城市规划陷入前所未有的危…...
金融工程--pine-script 入门
背景 脚本基本组成 指标 常见的趋势类指标:均线类(MAs)、支撑/压力位(Support/Resistance)、趋势线(Trend Lines)、趋势通道(Trend Channels)、一目均衡表(Ichimoku)和 艾略特波浪(ElliotWave)。 均线指标 策略 策略种类 在TradingView上,有许多交易…...
Vue3 跨标签页或跨窗口通信
在 Vue 应用中,跨标签页或跨窗口的通信通常涉及到两个或多个浏览器标签页之间的信息共享。由于每个标签页或窗口都是独立的 JavaScript 执行环境,它们不能直接通过 Vue 或其他 JavaScript 库来直接相互通信。但是,有一些方法可以实现这种跨标…...
Ollama: 使用Langchain的OllamaFunctions
1. 引言 Function call Langchain的Ollama 的实验性包装器OllamaFunctions,提供与 OpenAI Functions 相同的 API。因为网络的原因,OpenAI Functions不一定能访问,但如果能通过Ollama部署的本地模型实现相关的函数调用,还是有很好…...
java质数的判断 C语言指针变量的使用
1. public static void main(String[] args) {Scanner scnew Scanner(System.in);System.out.println("请输入一个值");int num sc.nextInt();boolean flagtrue;for (int i2;i<num;i){if (num%i0){flagfalse;break;}}if (flag){System.out.println(num"是一…...
TensorFlow面试整理-TensorFlow 数据处理
在 TensorFlow 中,数据处理是构建和训练深度学习模型的重要环节。高效地管理、预处理和增强数据可以显著提高模型的训练效率和性能。TensorFlow 提供了强大的 tf.data API 来帮助处理各种数据集。下面是 TensorFlow 数据处理的详细介绍: 1. tf.data.Dataset API tf.data API …...
vue路由的基本使用
vue路由的基本使用 vue-router简介一、路由配置和使用1、安装2、创建路由实例2、在组件中引用路由 router-view ,如APP根组件中直接引用:3、最后还需要把路由挂载到APP实例中,在main.js中注册路由: 二、路由重定向与别名三、声明式导航1、传统…...
数据结构分类
数据结构(data structure)是计算机存储、组织数据的方式,是带有结构特性的数据元素的集合。是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。这种“结构”指的是数据元素之间存在的关系,分为逻辑结构和存储…...
【STM32】 TCP/IP通信协议--LwIP介绍
LwIP(Lightweight IP)是一个轻量级的TCP/IP协议栈,专为嵌入式系统设计,以较小的资源消耗实现完整的网络功能。本文将详细介绍LwIP的基本概念、特点、与TCP/IP的区别以及如何在STM32上使用LwIP实现TCP/IP通信。 1. LwIP的定义和设…...
一些面试题整理
第一章、基础 以下是对上述10道面试题的参考答案: 一、Java语言及性能调优 答案: 线程安全问题是指多个线程同时访问共享资源时可能出现的数据不一致或错误的情况。例如,多个线程同时对一个共享变量进行写操作,如果没有适当的同…...
端口号和ip地址一样吗?区别是什么
在网络通信的世界里,端口号和IP地址是两个不可或缺的概念,它们各自扮演着独特的角色,共同维系着数据在网络中的有序传输。然而,对于许多初学者而言,这两者往往容易被混淆,认为它们是同一事物的不同表述。那…...
深入探讨全流量回溯分析与网络性能监控系统
AnaTraf 网络性能监控系统NPM | 全流量回溯分析 | 网络故障排除工具 随着数据量的急剧增加,传统的网络监控手段面临诸多挑战。在此背景下,全流量回溯分析和网络性能监控系统成为了保障网络正常运作的重要工具。本文将围绕这两个关键词,探讨它…...
python机器人编程——一种3D骨架动画逆解算法的启示(上)
目录 一、前言二、fabrik 算法三、python实现结论PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源ps3.移动小车相关文章资源ps3.wifi小车控制相关文章资源 一、前言 我们用blender等3D动画软件时,会用到骨骼的动画,通过逆向IK动力学…...
Flutter开发者必备面试问题与答案02
Flutter开发者必备面试问题与答案02 视频 https://youtu.be/XYSxTb0iA9I https://www.bilibili.com/video/BV1Zk2dYyEBr/ 前言 原文 Flutter 完整面试问题及答案02 本文是 flutter 面试问题的第二讲,高频问答 10 题。 正文 11. PageRoute 是什么? …...
拥抱真实:深度思考之路,行动力的源泉
在纷繁复杂的现代社会,人们往往被表象迷惑,忙碌于各种事务之中,却很少停下来进行深度思考。这种忙碌往往是表面的、无效的,因为它缺乏对自我和目标的深刻理解与追求。提升行动力,避免假勤奋,关键在于深度思…...
二手车网站模版售价/bt最佳磁力搜索引擎吧
package test /** * scala中的:: , :, :, :::, , 等操作; */ object listTest { def main(args: Array[String]): Unit { val list List(1,2,3) // :: 用于的是向队列的头部追加数据,产生新的列表, x::list,x就会添加到list的头部 println(4 :: list) //输…...
浪漫免费表白网站/湖南seo网站开发
以下所说的所有东西若要服用药物以补充,则不可过量服用,过量服用容易引起早熟(不一定会中毒),早熟则停止成长发育,导致身体只长宽度不长高度。 一、延缓骨骺线闭合 想要长高不要在骨骺线上下功夫。 因为在人体内激素是一个很麻烦的…...
网站建设项目内控单/百度首页 百度一下
1.top命令找到cpu占用最高的进程号 2.top -Hp 5363命令找到cpu占用最高的线程号 3.获取5363的16进制红框部分14f3:printf %x 5375 4.进入java的bin目录下执行命令: jstack 5363(进程号) |grep -A 200 1ad8(线程号16进制…...
手机网站建设哪家便宜/百度小说搜索风云榜排行榜
概述 byval是将实参所占的内存空间复制一份出来;byref是给实参另起一个别名,就好像是一个人两个名字。这两个的区别针对于一般的数据类型,大家都非常的清楚,但是,当用遇到对象类型的变量的时候,要小心了&am…...
创建个人网站英文/深圳网站建设优化
一、基础取值问题 例如<select class"selector"></select> 1、设置value为pxx的项选中 $(".selector").val("pxx"); 2、设置text为pxx的项选中 $(".selector").find("option:contains(pxx)").attr("select…...
做网站必须有框架是吗/seo外包公司兴田德润
录🦌🕐什么是字符串🕑基础知识🕔拼接字符串🕖字符串索引🕘字符串切片(重)🕚printf风格的字符串格式化🕛字符串常见的方法🕐什么是字符串 字符串是由 Unicode 码位构成的…...