当前位置: 首页 > article >正文

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

http://www.kler.cn/news/367531.html

相关文章:

  • 哪些CRM系统适合医疗行业?主流10款产品全解析
  • 深入理解Python异常处理机制
  • 视频美颜平台的搭建指南:基于直播美颜SDK的完整解决方案
  • 小白直接冲!一区蛇群优化算法+双向深度学习+注意力机制!SO-BiTCN-BiGRU-Attention多输入单输出回归预测
  • sheng的学习笔记-AI基础-正确率/召回率/F1指标/ROC曲线
  • 计算机网络原理总结C-网络层
  • 基于Python的智能求职分析系统
  • python 使用 企微机器人发送消息
  • 安全日志记录的重要性
  • 今天不分享技术,分享秋天的故事
  • Spring Boot框架下的厨艺社区开发
  • ALLO数据集:首个为月球轨道机器人近距离操作设计的异常检测基准开源数据集。
  • 安全知识见闻-脚本语言对与安全的重要性
  • Spring Boot驱动的厨艺分享社区开发
  • 5G工业路由器智能电网部署实录:一天内解决供电、网络
  • 手机在网状态查询接口-在线手机在网状态查询-手机在网状态查询API
  • vue2 关于组件
  • react mackDowan 渲染文本,图片,视频
  • Vue3实现获取验证码按钮倒计时效果
  • 深入解析机器学习算法
  • 阿里云申请免费域名证书流程
  • HTTPS讲解
  • Python基于TensorFlow实现循环神经网络GRU回归模型(GRU回归算法)项目实战
  • 一篇文章快速认识 YOLO11 | 目标检测 | 模型训练 | 自定义数据集
  • 校园资讯平台|校园资讯平台系统|基于java和小程序的校园资讯平台设计与实现(源码+数据库+文档)
  • 基于Asp.Net Core和Vue.js的Weblog系统的设计与实现