首页 > 其他分享 >2FA-双因素认证

2FA-双因素认证

时间:2024-10-24 11:21:48浏览次数:7  
标签:因素 demo springframework 认证 2FA org import security public

双因素认证(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

标签:因素,demo,springframework,认证,2FA,org,import,security,public
From: https://blog.csdn.net/qq_43673096/article/details/143204669

相关文章

  • CSP-S 2024 第二轮认证——游记
    CSP游记Day-3学校办运动会了,机房有勇夫参赛,第一轮OUT。FRZ_29大佬直接开卷,蹲守机房,泡面为伴,结果被无可奈何花落去搞得一天无可奈何。本蒟蒻play了一个上午,下午回到机房,发现FRZ_29大佬已经卷了一个上午,直接当场%%%%%。晚饭也是吃机房特产,精品美食泡面(bushi)。晚上尝试驯服Li......
  • Nginx配置auth_basic认证,让用户访问指定页面时输入用户名密码认证
    配置方法:[root@localhost~]#yum-yinstallhttpd-toolsLoadedplugins:fastestmirrorLoadingmirrorspeedsfromcachedhostfileResolvingDependencies-->Runningtransactioncheck--->Packagehttpd-tools.x86_640:2.4.6-99.el7.centos.1willbeinstall......
  • 电话号码如何认证显示公司名称?
    在科技迅猛发展的当下,智能手机已然成为人与人之间沟通交流的重要工具。如今,人们只需要拨通指定的电话号码,便能联系到相应的人。而对于企业来说,电话号码不仅仅是联系方式的象征,更是一种展示企业身份和品牌信誉的工具。想象一下,当企业拨打电话联系客户时,如果电话显示的是公司名称......
  • 【Azure Developer】使用JavaScript通过SDK进行monitor-query的client认证报错问题
    问题描述使用JavaScript通过SDK进行monitor-query的client初始化时候,需要进行认证时报错AADSTS90002。代码如下:constcredential=newDefaultAzureCredential();constlogsQueryClient=newLogsQueryClient(credential,{endpoint:"https://api.loganalytics.azur......
  • Oracle认证证书的考试费用是多少
    近期有学员咨询时问到:他大学学的是it和计算机方面的课程,在投简历时经常会看到Oracle认证优先,所以来问问Oracle证书的事情。新接触数据库行业的毕业生或者转行的人可能不清楚Oracle认证的含金量,Oracle是非常有名的数据库产品,在db-ranking统计中,Oracle数据库一直霸占第一的位置,Orac......
  • SpringBoot 单元测试 - 登录认证在 Spring Boot 上的标准单元测试写法。
    ......
  • K8s-安全认证
    访问控制概述Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作。客户端在Kubernetes集群中,客户端通常有两类:UserAccount:一般是独立于kubernetes之外的其他服务管理的用户......
  • flask影响电影票房因素的数据分析及可视化系统 毕业设计程序源码19201
    摘 要现在电影行业飞速发展,传统影响电影票房因素的数据分析及可视化方式己经逐渐跟不上时代变化的速度。在计算机行业发达的今天,希望利用现代爬虫技术的优势,提高数据分析及可视化效率及效果。本系统采用的是 Python 语言,使用 PyCharm 这一款开发工具,综合运用了 Tkinte......
  • 前后端实现双Token无感刷新用户认证
    前后端实现双Token无感刷新用户认证本文记录了使用双Token机制实现用户认证的具体步骤,前端使用的Vue,后端使用SpringSecurity和JWT双Token分别指的是AccessToken和RefreshTokenAccessToken:每次请求需要携带AccessToken访问后端数据,有效期短,减少AccessToken泄露带来的风险Refres......
  • 【数字化转型到底转了啥?】学习华为HCIP认证后谈谈华为的数字化转型
     背景: 最近正在学习华为HCIP认证相关课程,其中第一讲就是关于企业架构和数字化转型的课程。谈一谈总结和感想,华为的数字化转型,真的就像是一次华丽的蜕变,他们通过数字化转型,把业务都重新梳理了一遍,让效率变得超级高,客户体验也变得超级棒。这种变革,真的让人感受到了数字化转型......