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 功能:

  1. 访问 /totp-setup 端点生成 TOTP 密钥和 QR 码。
  2. 使用 Google Authenticator 扫描 QR 码。
  3. 访问 /totp-verify 端点并输入 Google Authenticator 生成的一次性密码。
  • 接口输出url可通过二下面工具生成
  • 二维码工具:https://www.runoob.com/try/try.php?filename=tryhtml5_QRCode

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在算家云搭建ControlNext-SVD(视频生成)

一、ControlNext-SVD-V2简介 ControlNext-SVD-V2 是 ControlNext-SVD 的 V2 模型。其中 ControlNext-SVD 模型是通过添加 ControlNet 来控制 Stable Video Diffusion (SVD)&#xff0c;使用高分辨率视频训练&#xff0c;具体来说它可以将图片生成与指定姿态相匹配的高质量视频…

Python异常检测- 单类支持向量机(One-Class SVM)

系列文章目录 Python异常检测- Isolation Forest&#xff08;孤立森林&#xff09; python异常检测 - 随机离群选择Stochastic Outlier Selection (SOS) python异常检测-局部异常因子&#xff08;LOF&#xff09;算法 Python异常检测- DBSCAN 文章目录 系列文章目录前言一、On…

1024程序员日|向改变世界的程序员 致敬!

“给我一行代码&#xff0c;我将点亮整个服务器” “给我一个键盘&#xff0c;我就能征服数字世界” 今天 10月24号 是广大程序员“法定”节日&#xff08;据说是自定的&#xff09; 因为1024是2的十次方 二进制计数的基本计量单位 1GB 1024MB&#xff0c;1MB 1024KB……

机器学习理论系列——线性模型(上)

系列文章目录 文章目录 线性模型线性回归线性回归与线性模型一元和多元线性回归最小二乘法损失函数与均方误差最小二乘与闭式解 正则化为什么引入正则化 L 1 L^1 L1&#xff0c; L 2 L^2 L2正则化 梯度下降什么是梯度下降算法上的体现 附录 线性模型 线性是数学中的基本概念&a…

面试官:Zabbix 和 Prometheus 到底怎么选?

新公司要上监控&#xff0c;面试提到了 Prometheus 是公司需要的监控解决方案&#xff0c;我当然是选择跟风了。 之前主要做的是 Zabbix&#xff0c;既然公司需要 Prometheus&#xff0c;那没办法&#xff0c;只能好好对比一番&#xff0c;了解下&#xff0c;毕竟技多不压身。…

家庭宽带的ip地址是固定的吗?宽带ip地址怎么修改‌

在家庭网络环境中&#xff0c;IP地址的分配和管理是用户常关注的问题。本文将探讨家庭宽带IP地址的固定性&#xff0c;并介绍如何修改宽带IP地址&#xff0c;以满足用户的不同需求。 一、家庭宽带的IP地址是否固定? 关于家庭宽带的IP地址是否固定&#xff0c;答案并非一概而论…

Could not find artifact cn.hutool:hutool-all:jar:8.1 in central 导入Hutool报错

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.9</version></dependency> 引入hutool 8.1版本的工具…

采用 Redis+数据库为建立线上药品销售系统

目录 案例 【说明】 【问题1】(9分) 【问题2】(9分) 【问题3】(7 分) 【答案】 【问题1】答案 【问题2】答案 【问题3】答案 相关推荐 案例 阅读以下关于数据库设计的叙述&#xff0c;回答问题 1 至问题 3 。 【说明】 某医药销售企业因业务发展&#xff0c;需要建立…

Windows图形界面(GUI)-QT-C/C++ - QT基本概念 - 安装配置 - QT Creator

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 QT 基本概念 QT 安装配置 QT Creator 界面布局 快捷操作 QT 基本概念 QT 简介 QT是一个跨平台的C图形用户界面应用程序开发框架。 QT支持多种操作系统&#xff0c;包括Windows、Lin…

2024年必收藏!最全 禅道 项目管理软件各版本安装部署全攻略

一、禅道简介 1. 禅道是什么&#xff1f;功能强大的项目管理软件。 禅道是一款专业的国产开源研发项目管理软件&#xff0c;集产品管理、项目管理、质量管理、文档管理、组织管理和事务管理于一体&#xff0c;完整覆盖了研发项目管理的核心流程。 管理思想基于国际流行的敏捷…

Ovis: 多模态大语言模型的结构化嵌入对齐

论文题目&#xff1a;Ovis: Structural Embedding Alignment for Multimodal Large Language Model 论文地址&#xff1a;https://arxiv.org/pdf/2405.20797 github地址&#xff1a;https://github.com/AIDC-AI/Ovis/?tabreadme-ov-file 今天&#xff0c;我将分享一项重要的研…

咸鱼自动发货 免费无需授权

下载&#xff1a;&#xff08;两个都可以下&#xff0c;自己选择&#xff09; https://pan.quark.cn/s/1e3039e322ad https://pan.xunlei.com/s/VO9ww89ZNkEg_Fq1wRr-fk9ZA1?pwd8x9s# 不是闲管家 闲鱼自动发货&#xff08;PC端&#xff09; 暂不支持密&#xff0c;免费使…

论文阅读与写作入门

文章目录 1.阅读第一篇论文(1)论文结构(2)目标 2.使用GPT辅助论文的阅读与写作3.专有名词(1)架构(2)网络(3)机器学习 4.文献翻译软件5.如何判断(你自己的)研究工作的价值or贡献【论文精读李沐】6.经典论文(1)AlexNet 2012(2)FCN 全卷积 2014(3)ResNet&#xff1a;残差学习&…

【Spring篇】Spring的Aop详解

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】【Mybatis篇】【Spring篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;初始Sprig AOP及…

通过运行窗口呼出Windows功能的快捷命令集合

平时使用电脑需要快速调出Windows的一些功能设置&#xff0c;你们是怎么样操作的呢&#xff1f;今天给大家归集一些通过运行窗口快速调出Windows功能的快捷命令&#xff0c;供朋友们参考。示例如下图&#xff0c;各个功能命令集合见表格.

Python实现贪吃蛇大作战

初始版本 初始版本&#xff0c;只存在基本数据结构——双向队列。 游戏思路 贪吃蛇通过不断得吃食物来增长自身&#xff0c;如果贪吃蛇碰到边界或者自身则游戏失败。 食物是绿色矩形来模拟&#xff0c;坐标为随机数生成&#xff0c;定义一个蛇长变量&#xff0c;判断蛇头坐标和…

需求分析基础指南:从零开始理解需求分析

目录 从零开始理解需求分析什么是需求分析&#xff1f;需求分析的目标需求分析的基本原则需求分析的各个阶段需求分析的常用方法和工具编写需求文档总结 从零开始理解需求分析 需求分析是软件开发过程中不可或缺的一环&#xff0c;它帮助我们明确用户的需求&#xff0c;确保最…

养殖场大型全自动饲料颗粒加工机械设备

随着养殖业的快速发展&#xff0c;对饲料加工设备的需求也日益增长。全自动饲料颗粒机作为现代养殖场的重要制粒设备&#xff0c;其自动化、高效化和智能化特点&#xff0c;不仅提高了饲料生产效率&#xff0c;还保障了饲料质量&#xff0c;为养殖业的可持续发展提供了有力支持…

关于jmeter中没有jp@gc - response times over time

1、问题如下&#xff1a; jmeter没有我们要使用的插件 2、解决方法&#xff1a; 选择下面文件&#xff0c;点击应用&#xff1b; 3、问题解决 ps&#xff1a;谢谢观看&#xff01;&#xff01;&#xff01;