【2FA】双重身份验证

双重身份验证(Two-Factor Authentication,简称2FA)是一种安全措施,旨在通过要求用户提供两种不同类型的身份验证信息来增强账户访问的安全性。这通常包括用户知道的东西(知识因素)和用户拥有的东西( possession factor),或者用户本身的特征( inherence factor)。双重认证结合了两个不同类别的验证,即使攻击者获取到了其中一个因素,也很难同时获取另一个因素,从而大大降低了账户被盗用的风险。

用户输入用户名和密码(第一重验证)。
系统要求用户通过第二种验证方式提供额外的信息或响应,如输入从手机收到的验证码、生成的动态口令或进行生物特征扫描。
一旦两个验证步骤都成功完成,用户才能被授权访问系统或服务。

这里采用的是应用生成的验证码(Authenticator App):如Google Authenticator、Microsoft Authenticator等应用生成基于时间或计数器的一次性密码(TOTP)。

基于时间的一次性密码(TOTP)

  • TOTP是一种动态密码生成算法,它依赖于当前时间来产生一次性有效的密码。其工作原理如下:

    时间步长:系统使用一个固定的时间间隔(通常是30秒)作为时间步长,每个时间步长内生成一个唯一的密码。
    密钥:用户和服务之间共享一个秘密密钥(Secret Key),这个密钥在初始化时通过安全的方式分发,比如通过扫描二维码。
    哈希算法:使用如HMAC-SHA-1、SHA-256等加密哈希算法,结合当前时间戳和共享密钥计算出一个哈希值,然后取哈希值的一部分转换成易于用户输入的形式,通常是一个6位数字。
    时间同步:客户端(如手机上的身份验证应用)和服务端都需要保持大致相同的时间,以确保在正确的时间步内验证密码。
    基于计数器的一次性密码(COTP)
    COTP与TOTP相似,但不是基于时间而是基于递增的计数器(Counter)来生成密码。其特点包括:

  • 计数器:每次生成新密码时,客户端和服务端的计数器都会递增。计数器的初始值 和步长在初始化时协商确定。
    密钥与哈希:同样使用共享密钥和哈希算法,不过这次是结合当前计数器的值而非时间戳来计算哈希。
    同步问题:相比TOTP,COTP对时间同步的要求较低,但在计数器不同步的情况下(例如,如果客户端长时间离线后再上线),需要有机制处理计数器的重新同步问题。

使用

1. 注册Config

import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import dev.samstevens.totp.code.DefaultCodeVerifier;
import dev.samstevens.totp.code.HashingAlgorithm;
import dev.samstevens.totp.qr.QrDataFactory;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.qr.ZxingPngQrGenerator;
import dev.samstevens.totp.secret.DefaultSecretGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.time.SystemTimeProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TwoFAConfig {

    @Bean
    public SecretGenerator secretGenerator() {
        return new DefaultSecretGenerator();
    }

    @Bean
    public QrDataFactory qrDataFactory() {
        return new QrDataFactory(HashingAlgorithm.SHA1, 6, 30);
    }

    @Bean
    public QrGenerator qrGenerator() {
        return new ZxingPngQrGenerator();
    }

    @Bean
    public CodeVerifier codeVerifier() {
        return new DefaultCodeVerifier(new DefaultCodeGenerator(HashingAlgorithm.SHA1, 6), new SystemTimeProvider());
    }
}

2. Service

  • setupDevice()用于生成QR码和密钥,完成和Microsoft Authenticator客户端的初始绑定。
  • codeVerifier.isValidCode(secret, code) 验证提供的code和真实的密码是否一致。
import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrDataFactory;
import dev.samstevens.totp.qr.QrGenerator;
import im.gy.zfile.core.exception.LoginVerifyException;
import im.gy.zfile.module.config.model.dto.SystemConfigDTO;
import im.gy.zfile.module.config.service.SystemConfigService;
import im.gy.zfile.module.login.model.enums.LoginVerifyModeEnum;
import im.gy.zfile.module.login.model.request.VerifyLoginTwoFactorAuthenticatorRequest;
import im.gy.zfile.module.login.model.result.LoginTwoFactorAuthenticatorResult;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import dev.samstevens.totp.secret.SecretGenerator;

import static dev.samstevens.totp.util.Utils.getDataUriForImage;

/**
 * 2FA 双因素认证 Service
 */
@Service
public class TwoFactorAuthenticatorVerifyService {

    @Resource
    private SecretGenerator secretGenerator;

    @Resource
    private QrDataFactory qrDataFactory;

    @Resource
    private QrGenerator qrGenerator;

    @Resource
    private CodeVerifier codeVerifier;

    @Resource
    private SystemConfigService systemConfigService;

    /**
     * 生成2FA 双因素认证二维码和密钥
     * @return
     * @throws QrGenerationException
     */
    public LoginTwoFactorAuthenticatorResult setupDevice() throws QrGenerationException {
        // 生成的2FA密钥
        String secret = secretGenerator.generate();
        QrData data = qrDataFactory.newBuilder().secret(secret).issuer("ZFile").build();

        // 密钥转base64图像字符串
        String qrCodeImg = getDataUriForImage(
                qrGenerator.generate(data),
                qrGenerator.getImageMimeType()
        );
        return new LoginTwoFactorAuthenticatorResult(qrCodeImg, secret);
    }

    /**
     * 验证 2FA 双因素是否正确。正确则进行绑定
     * @param verifyLoginTwoFactorAuthenticatorRequest
     */
    public void deviceVerify(VerifyLoginTwoFactorAuthenticatorRequest verifyLoginTwoFactorAuthenticatorRequest) {
        String code = verifyLoginTwoFactorAuthenticatorRequest.getCode();
        String secret = verifyLoginTwoFactorAuthenticatorRequest.getSecret();
        if (codeVerifier.isValidCode(secret, code)) {
            SystemConfigDTO systemConfig = systemConfigService.getSystemConfig();
            systemConfig.setLoginVerifyMode(LoginVerifyModeEnum.TWO_FACTOR_AUTHENTICATION_MODE);
            systemConfig.setLoginVerifySecret(secret);
            systemConfigService.updateSystemConfig(systemConfig);
        } else {
            throw new LoginVerifyException("验证码错误");
        }
    }

    public void checkCode(String loginVerifySecret, String verifyCode) {
        if (!codeVerifier.isValidCode(loginVerifySecret, verifyCode)) {
            throw new LoginVerifyException("验证码错误或已经失效");
        }
    }
}
  • 23
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CRE_MO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值