Spring Security 多因素认证(MFA)

这篇具有很好参考价值的文章主要介绍了Spring Security 多因素认证(MFA)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Spring Security 系列文章开始更新了!工程地址为github.com/ReLive27/sp…,如果你对此系列感谢趣,可以点击关注作者获取最新文章发布信息。

多因素身份验证是一种提高产品安全性的方法,它通过要求用户提供除用户名和密码之外的第二种形式的身份验证来增加额外的安全层。

在本文中,我们将使用 TOTP(基于时间的一次性密码)作为第二种身份识别形式。此 TOTP 由用户移动设备上的应用程序生成,例如 Google 身份验证器。

💡 注意:如果不想读到最后,可以在这里查看源码。喜欢的话别忘了给项目一个star哦!

多因素身份验证的工作原理

当用户启用多因素身份验证时,将生成一个密钥并以 QR 码的形式发送给用户,用户将使用身份验证器应用程序对其进行扫描。

登录过程现在需要几个步骤:

1.用户输入用户名和密码。

2.身份验证服务验证用户名和密码。

3.用户通过身份验证器应用程序扫描 QR 码。

4.用户输入验证器应用程序生成的一次性密码。

5.身份验证服务使用生成的密钥验证一次性密码,并将 JWT 令牌发送给用户。

让我们深入了解实施。

一次性密码管理器

我们在 pom.xml 文件中引入该库 用于生成密钥并验证一次性密码。

<dependency>
    <groupId>dev.samstevens.totp</groupId>
    <artifactId>totp-spring-boot-starter</artifactId>
    <version>1.7.1</version>
</dependency>

DefaultTotpManager 包装了TOTP库,它有以下操作:

public class DefaultTotpManager implements MfaAuthenticationManager {

    @Override
    public String generateSecret() {}

    @Override
    public String getUriForImage(String label, String secret, String issuer) throws QrGenerationException {}

    @Override
    public boolean validCode(String secret, String code) {}
}

首先,生成密钥,第二,生成密钥的二维码图像 URI,最后,validCode 验证提供的代码是正确的还是错误的代码。

这些方法的实现是直接使用 TOPT 库。

一次性密码验证流程

提交一次性密码后,MfaAuthenticationFilter 将对一次性密码进行验证,我们遵循 Spring Security 的认证架构,
因此下图看起来应该非常相似 AbstractAuthenticationProcessingFilter :

Spring Security 多因素认证(MFA),Spring Security,java,spring boot,后端,安全

1: 当用户提交一次性密码时,在实例 MfaAuthenticationFilter 通过 MfaAuthenticationConverterHttpServletRequest 创建一个 MfaAuthenticationToken ,这是一种Authentication类型。
MfaAuthenticationConverter 将从 SecurityContextHolder.getContext().getAuthentication() 获取 Authentication,若 Authentication 不为空,执行 setAuthenticated(false) ,在一次性密码验证成功后重新置为true。

2: 接下来 MfaAuthenticationToken 将传递给 AuthenticationManager 进行验证。ProviderManager是最常用的AuthenticationManager实现类。 ProviderManager将验证委托给一个AuthenticationProviderList实例。这里我们
使用MfaAuthenticationProvider执行一次性密码验证。

3: 如果验证失败,则为FailureAuthenticationFailureHandler被调用,响应JSON格式的错误信息。

4: 如果验证成功,则为SuccessAuthenticationSuccessHandler被调用,在MfaAuthenticationTokenContextHolder设置MfaTokenContext上下文信息,包含一次性密码验证成功信息。

用户服务

我们实现UserDetails接口创建一个MfaUserDetails模型增加两个新属性,如下所示:

public class MfaUserDetails implements UserDetails {
    ...

    private final boolean enableMfa;
    private String secret;

}

第一个是标志,指示是否启用双因素身份验证,第二个是保存密钥的字符串。

InMemoryMfaUserDetailsManager实现 UserDetailsService为存储在内存中的基于用户名/密码的身份验证提供支持。
InMemoryMfaUserDetailsManager 通过实现 UserDetailsManager 接口提供对 MfaUserDetails 的管理。如下所示:

public class InMemoryMfaUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {
    private final Map<String, UserDetails> users = new HashMap<>();
    private AuthenticationManager authenticationManager;


    public InMemoryMfaUserDetailsManager() {
    }

    public InMemoryMfaUserDetailsManager(UserDetails... users) {
        UserDetails[] userDetails = users;
        int length = users.length;

        for (int i = 0; i < length; ++i) {
            UserDetails user = userDetails[i];
            this.createUser(user);
        }
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        MfaUserDetails user = (MfaUserDetails) this.users.get(username.toLowerCase());
        if (user == null) {
            throw new UsernameNotFoundException(username);
        } else {
            return new MfaUserDetails(user.getUsername(), user.getPassword(), user.isEnableMfa(), user.getSecret(), user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(), user.getAuthorities());
        }
    }

    @Override
    public void createUser(UserDetails user) {
        Assert.isTrue(!this.userExists(user.getUsername()), "user should not exist");
        this.users.put(user.getUsername().toLowerCase(), user);
    }

    @Override
    public void updateUser(UserDetails user) {
        Assert.isTrue(this.userExists(user.getUsername()), "user should exist");
        this.users.put(user.getUsername().toLowerCase(), user);
    }

    @Override
    public void deleteUser(String username) {
        this.users.remove(username.toLowerCase());
    }

    ...
}

登录流程

本节我们启用 Spring Security 的基于表单的登录。

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
                .formLogin();

        //...

        return http.build();
    }

接下来我们需要更改表单登录的默认AuthenticationSuccessHandler实现类,我们创建MfaAuthenticationSuccessHandler实现AuthenticationSuccessHandler

public class MfaAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    ...

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) authentication;
        MfaUserDetails userDetails = (MfaUserDetails) authenticationToken.getPrincipal();
        if (userDetails.isEnableMfa()) {

            if (!StringUtils.hasText(userDetails.getSecret())) {
                String secret = mfaAuthenticationManager.generateSecret();
                userDetails.setSecret(secret);
                this.userDetailsManager.updateUser(userDetails);
                String uriForImage;
                try {
                    uriForImage = mfaAuthenticationManager.getUriForImage(userDetails.getUsername(), secret, "http://127.0.0.1:8080");
                } catch (Exception e) {
                    log.error("Error getting QR code image", e);
                    MfaAuthenticationResponse mfaAuthenticationResponse = MfaAuthenticationResponse.unauthenticated("Error getting QR code image", "bind", HttpStatus.BAD_REQUEST, null);
                    this.sendMfaResponse(request, response, mfaAuthenticationResponse);
                    return;
                }
                MfaAuthenticationResponse mfaAuthenticationResponse = MfaAuthenticationResponse.unauthenticated("The current account is not bound to the token app", "bind", HttpStatus.OK, uriForImage);
                this.sendMfaResponse(request, response, mfaAuthenticationResponse);
                return;
            }
            MfaTokenContext mfaTokenContext = MfaAuthenticationTokenContextHolder.getMfaTokenContext();
            if (mfaTokenContext == null || !mfaTokenContext.isMfa()) {
                MfaAuthenticationResponse mfaAuthenticationResponse = MfaAuthenticationResponse.unauthenticated("dynamic password error", "enable", HttpStatus.OK, null);
                this.sendMfaResponse(request, response, mfaAuthenticationResponse);
                return;
            }
        }

        Jwt jwt = this.tokenGenerator.generate(authentication);
        MfaAuthenticationResponse mfaAuthenticationResponse = MfaAuthenticationResponse.authenticated(userDetails.isEnableMfa() ? "enable" : "disabled", jwt.getTokenValue());
        this.sendMfaResponse(request, response, mfaAuthenticationResponse);
    }

    ...
}

此过程中重要几个步骤:

  1. 用户是否启用多因素认证,若未启用则直接生成JWT格式令牌。
  2. 用户已经启用多因素认证,判断当前用户是否已生成密钥,若未生成密钥,则创建64位密钥,并响应 QR 码提供给用户进行绑定。
  3. MfaAuthenticationTokenContextHolder获取MfaTokenContext上下文信息,判断一次性密码是否验证通过。
  4. 一次性密码验证通过,最终生成JWT格式令牌。

最终 Spring Security 安全配置为:

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
                .formLogin().successHandler(MfaConfigurerUtils.getAuthenticationSuccessHandler(http));

                ...

        return http.build();
    }

演示

下面是我们的最终实现目标,前端工程使用 Vue 实现,您可以从这里找到完整的前端源代码。

Spring Security 多因素认证(MFA),Spring Security,java,spring boot,后端,安全

结论

多因素身份验证通过添加额外的安全层来提高安全级别,从而增加信任并使攻击者更难访问您的数据。

与往常一样,本文中使用的源代码可在 GitHub 上获得。文章来源地址https://www.toymoban.com/news/detail-721559.html

到了这里,关于Spring Security 多因素认证(MFA)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 多因素身份认证 (MFA) 插件:手机验证码认证因素配置流程

    多因素身份认证 (MFA) 插件:手机验证码认证因素配置流程

    对用户表扩展手机号码字段,允许用户通过手机号码与验证码的方式进行认证,注册,重置密码以及更换手机号。 多因素身份认证 (MFA) 是保护企业 IT 资源访问安全的一种关键工具,也是零信任安全模型的核心组成。特别在远程办公以及数据泄露事件层出不穷的背景下,越来

    2024年02月09日
    浏览(37)
  • Spring Authorization Server入门 (八) Spring Boot引入Security OAuth2 Client对接认证服务

    Spring Authorization Server入门 (八) Spring Boot引入Security OAuth2 Client对接认证服务

    在之前的文章中实现了一个认证服务,并且添加了一些自定义的内容,现在暂时没想到认证服务的新内容,本篇文章就先写一下客户端对接的吧,水一篇。 当用户通过客户端去访问一个受限的资源时,客户端会检测是否有登录信息,没有登录信息会重定向至认证服务器去请求

    2024年02月21日
    浏览(18)
  • 使用Spring Boot Security 实现多认证 手机号登录 微信扫码登录 微信扫码注册

    使用Spring Boot Security 实现多认证 手机号登录 微信扫码登录 微信扫码注册

    Spring Boot 3.x Spring Security 5.7 Spring Redis MyBatis plus 前端 Vue 公司 最近有个新项目 使用单点登录 sso 百度了一圈 也没怎么找到微信扫码注册的功能于是自己写 需求就是 手机 + 密码登录 微信扫码登录 微信扫码注册 微信二维码 登录 和注册二合一 具体实现 稍后我会说 本教程将指导

    2024年04月10日
    浏览(15)
  • Java21 + SpringBoot3使用Spring Security时如何在子线程中获取到认证信息

    Java21 + SpringBoot3使用Spring Security时如何在子线程中获取到认证信息

    目录 前言 原因分析 解决方案 方案1:手动设置线程中的认证信息 方案2:使用 DelegatingSecurityContextRunnable 创建线程 方案3:修改 Spring Security 安全策略 通过设置JVM参数修改安全策略 通过 SecurityContextHolder 修改安全策略 总结 近日心血来潮想做一个开源项目,目标是做一款可以适

    2024年02月19日
    浏览(10)
  • 【Spring Security】Spring Security 认证与授权

    【Spring Security】Spring Security 认证与授权

    在前面的章节中,我们沿用了Spring Security默认的安全机制:仅有一个用户,仅有一种角色。在实际开发中,这自然是无法满足需求的。本章将更加深入地对Spring Security迚行配置,且初步使用授权机制。 3.1 默认数据库模型的认证与授权 3.1.1、资源准备 首先,在controller包下新建

    2024年02月05日
    浏览(9)
  • 微信小程序的授权登录-Java 后端 (Spring boot)

    微信开发文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 一个可以测试的微信小程序 此微信小程序的APPID和APPscret(至开发者后台获取) 从时序图我们可以了解到流程大致分为两步: 小程序端获取code后传给Java后台 Java后台获取code后向微信后台接口

    2024年02月09日
    浏览(48)
  • “从零开始学习Spring Boot:快速搭建Java后端开发环境“

    标题:从零开始学习Spring Boot:快速搭建Java后端开发环境 摘要:本文将介绍如何从零开始学习Spring Boot,并详细讲解如何快速搭建Java后端开发环境。通过本文的指导,您将能够快速搭建一个基于Spring Boot的Java后端开发环境并开始编写代码。 正文: 一、准备工作 在开始之前,

    2024年02月15日
    浏览(15)
  • Spring Security认证研究

    Spring Security认证研究

     1.统一认证  认证通过由认证服务向给用户颁发令牌,相当于访问系统的通行证,用户拿着令牌去访问系统的资源。  2.单点登录,对于微服务项目,因为包含多个模块,所以单点登录就是使得用户可以在认证一次的情况下就可以访问所有的拥有对应权限的服务。 3.第三方认

    2023年04月08日
    浏览(11)
  • spring security认证授权流程

    认证和授权是任何安全体系中的两个主要功能,而在现代Web开发中,Spring Security是最受欢迎和广泛使用的安全框架之一。在本篇文章中,我们将全面介绍Spring Security的认证和授权机制,并提供详细的步骤和示例代码。  一、认证(Authentication) 认证的主要目的是验证用户的身

    2024年02月15日
    浏览(10)
  • 【Spring Security系列】Spring Security 过滤器详解与基于JDBC的认证实现

    【Spring Security系列】Spring Security 过滤器详解与基于JDBC的认证实现

    上文说到,Spring Security它是一个强大的和高度可定制的身份验证和访问控制框架。它提供了一套丰富的功能,用于保护基于Spring的应用程序。 上文又说到,在Spring Security中,过滤器(Filter)是一个重要的组件,用于处理身份验证、授权和其他安全相关的任务。 Spring Security 的

    2024年04月22日
    浏览(16)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包