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"是一…...
 
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
 
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
 
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
 
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
 
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解
文章目录 一、开启慢查询日志,定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...
