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"是一…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...