使用Microsoft-Authenticator做系统登录的二次身份验证(双因素认证)

这篇具有很好参考价值的文章主要介绍了使用Microsoft-Authenticator做系统登录的二次身份验证(双因素认证)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概念:

二次验证这个功能又叫双因素认证_百度百科或者叫双因子认证

双因素认证是一种采用时间同步技术的系统,采用了基于时间、事件和密钥三变量而产生的一次性密码来代替传统的静态密码。每个动态密码卡都有一个唯一的密钥,该密钥同时存放在服务器端,每次认证时动态密码卡与服务器分别根据同样的密钥,同样的随机参数(时间、事件)和同样的算法计算了认证的动态密码,从而确保密码的一致性,从而实现了用户的认证。因每次认证时的随机参数不同,所以每次产生的动态密码也不同。由于每次计算时参数的随机性保证了每次密码的不可预测性,从而在最基本的密码认证这一环节保证了系统的安全性。解决因口令欺诈而导致的重大损失,防止恶意入侵者或人为破坏,解决由口令泄密导致的入侵问题。

我最近在项目中遇到需要在登录的时候加入双因素验证系统的功能需求, 整体步骤感觉还是有必要写下来,方便做类似功能的同学能够借鉴。

google-Authenticator在国内可能并不适用,于是需要寻找一个能在国内稳定使用的二次验证系统

微软的Microsoft-Authenticator二次验证系统可以说是比较合适的

官方的二次验证的流程如下:

使用Microsoft-Authenticator做系统登录的二次身份验证(双因素认证),Java实用工具,java,spring cloud,mybatis,gradle

工具类

这个工具类就放在你的项目中,就代表你的这个功能做了有一半了

getQrCodeText方法是生成二维码的内容

getQrCode方法是生成二维码

@Component
public class TwoFactorAuthUtil {

    @Autowired
    private ParameterStubV1 parameterStubV1;

    /**
     * 生成二维码内容    网站地址(可不写)
     * @return
     */
    public static String getQrCodeText(String secretKey, String account, String issuer) {
        String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase();
        try {
            String Url = null;
            Url = "otpauth://totp/"
                    + URLEncoder.encode((!StringUtils.isEmpty(issuer) ? (issuer + ":") : "") + account, "UTF-8").replace("+", "%20")
                    + "?secret=" + URLEncoder.encode(normalizedBase32Key, "UTF-8").replace("+", "%20")
                    + (!StringUtils.isEmpty(issuer) ? ("&issuer=" + URLEncoder.encode(issuer, "UTF-8").replace("+", "%20")) : "");
            //log.info(Url);
            return Url;
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * description:
     * @author 获取二维码
     */
    public String getQrCode(String loginName, String newSecretKey) {
        String base64Image = null;
        try {
            // 生成二维码内容
            String qrCodeText = getQrCodeText(newSecretKey, loginName, "");
            int width = 300; // 图片宽度
            int height = 300; // 图片高度
            try {
                // 将URL转换为BitMatrix
                QRCodeWriter qrCodeWriter = new QRCodeWriter();
                BitMatrix bitMatrix = qrCodeWriter.encode(qrCodeText, BarcodeFormat.QR_CODE, width, height);
                // 将BitMatrix转换为BufferedImage
                BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
                // 保存二维码图片到本地文件
                //File file = new File("D:\\图片\\qrcode.png");
                //ImageIO.write(bufferedImage, format, file);
                //log.info("QR Code image generated successfully!");

                // 生成二维码图像
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                MatrixToImageWriter.writeToStream(bitMatrix, "png", outputStream);

                // 获取图像的字节数组,并使用 Base64 编码转换成字符串
                byte[] imageData = outputStream.toByteArray();
                base64Image = AppConfigUtil.getStringValue(TwoFactorAuthConstant.MICROSOFT_AUTH_BASE64_IMAGE) + java.util.Base64.getEncoder().encodeToString(imageData);
                return base64Image;
            } catch (WriterException e) {
                throw new BusinessException(I18nMsg.tr("生成二维码图像失败"));
            } catch (Exception e) {
                throw new BusinessException("Unexpected error:" + e.getMessage());
            }
        } catch (BusinessException e) {
            throw new BusinessException(I18nMsg.tr("生成二维码发生异常:{0}", e.getMessage()));
        }
    }
}

 二次验证的接口:

这个是策略类的接口,除了使用微软的验证器,你还可以实现这个接口来使用其他品牌的验证器

public interface TwoFactorAuthProvider {
    //生成密钥
    String getSecretKey();

    //校验密钥
    boolean checkCode(String secret, long code, long time);
}

 实现类代码如下:

实现类主要是以下几个重要内容
      时间前后偏移量
      用于防止客户端时间不精确导致生成的TOTP与服务器端的TOTP一直不一致
      如果为0,当前时间为 10:10:15
      则表明在 10:10:00-10:10:30 之间生成的TOTP 能校验通过
      如果为1,则表明在
      10:09:30-10:10:00
      10:10:00-10:10:30
      10:10:30-10:11:00 之间生成的TOTP 能校验通过,以此类推

  private static int WINDOW_SIZE = 1;


     加密方式,HmacSHA1、HmacSHA256、HmacSHA512

private static final String CRYPTO = "HmacSHA1";

 getSecretKey方法用来生成用户密钥,生成的密钥记得做持久化

checkCode方法用来校验验证码是否正确

@Service
public class MicrosoftAuth implements TwoFactorAuthProvider {
    // 私有构造方法
    private MicrosoftAuth() {
    }

    // 在成员位置创建该类的对象
    private static MicrosoftAuth microsoftAuth;

    // 对外提供静态方法获取该对象
    public static synchronized MicrosoftAuth getMicrosoftAuth() {
        if (microsoftAuth == null) {
            microsoftAuth = new MicrosoftAuth();
        }
        return microsoftAuth;
    }

    /**
     * 时间前后偏移量
     * 用于防止客户端时间不精确导致生成的TOTP与服务器端的TOTP一直不一致
     * 如果为0,当前时间为 10:10:15
     * 则表明在 10:10:00-10:10:30 之间生成的TOTP 能校验通过
     * 如果为1,则表明在
     * 10:09:30-10:10:00
     * 10:10:00-10:10:30
     * 10:10:30-10:11:00 之间生成的TOTP 能校验通过
     * 以此类推
     */
    private static int WINDOW_SIZE = 1;

    /**
     * 加密方式,HmacSHA1、HmacSHA256、HmacSHA512
     */
    private static final String CRYPTO = "HmacSHA1";

    /**
     * description: 生成密钥,每个用户独享一份密钥
     *
     * @return java.lang.String
     * @author litong
     * @date 2023/4/27
     */
    @Override
    public String getSecretKey() {
        String secretKey = null;
        try {
            SecureRandom random = new SecureRandom();
            byte[] bytes = new byte[20];
            random.nextBytes(bytes);
            Base32 base32 = new Base32();
            secretKey = base32.encodeToString(bytes).toUpperCase();
        } catch (Exception e) {
            throw new BusinessException(I18nMsg.tr("生成密钥发生异常:{0}", e.getMessage()));
        }
        // make the secret key more human-readable by lower-casing and
        // inserting spaces between each group of 4 characters
        return secretKey;
    }

    /**
     * 检验 code 是否正确
     *
     * @param secret 密钥
     * @param code   code
     * @param time   时间戳
     * @return
     */
    @Override
    public boolean checkCode(String secret, long code, long time) {
        boolean flag = false;
        try {
            Base32 codec = new Base32();
            byte[] decodedKey = codec.decode(secret);
            // convert unix msec time into a 30 second "window"
            // this is per the TOTP spec (see the RFC for details)
            long t = (time / 1000L) / 30L;
            // Window is used to check codes generated in the near past.
            // You can use this value to tune how far you're willing to go.
            long hash;
            for (int i = -WINDOW_SIZE; i <= WINDOW_SIZE; ++i) {
                try {
                    hash = verifyCode(decodedKey, t + i);
                } catch (Exception e) {
                    // Yes, this is bad form - but
                    // the exceptions thrown would be rare and a static
                    // configuration problem
                    // e.printStackTrace();
                    throw new RuntimeException(e.getMessage());
                }
                if (hash == code) {
                    flag = true;
                    return flag;
                }
            }
        } catch (RuntimeException e) {
            throw new BusinessException(I18nMsg.tr("校验密钥发生异常:{0}", e.getMessage()));
        }
        return flag;
    }

    /**
     * 根据时间偏移量计算
     *
     * @param key
     * @param t
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    private static long verifyCode(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException {
        try {
            byte[] data = new byte[8];
            long value = t;
            for (int i = 8; i-- > 0; value >>>= 8) {
                data[i] = (byte) value;
            }
            SecretKeySpec signKey = new SecretKeySpec(key, CRYPTO);
            Mac mac = Mac.getInstance(CRYPTO);
            mac.init(signKey);
            byte[] hash = mac.doFinal(data);
            int offset = hash[20 - 1] & 0xF;
            // We're using a long because Java hasn't got unsigned int.
            long truncatedHash = 0;
            for (int i = 0; i < 4; ++i) {
                truncatedHash <<= 8;
                // We are dealing with signed bytes:
                // we just keep the first byte.
                truncatedHash |= (hash[offset + i] & 0xFF);
            }
            truncatedHash &= 0x7FFFFFFF;
            truncatedHash %= 1000000;
            return truncatedHash;
        } catch (NoSuchAlgorithmException | InvalidKeyException | IllegalStateException e) {
            throw new BusinessException(I18nMsg.tr("生成系统验证密钥出现异常{0}",e.getMessage()));
        }
    }
}

 总结

简单应用到系统中的步骤如下:

1.定义工具类和实现类

2.编写获取用户名密钥的接口,生成的密钥做持久化

3.使用工具类将密钥和用户名生成二维码,返回给前端,显示大概如下:

使用Microsoft-Authenticator做系统登录的二次身份验证(双因素认证),Java实用工具,java,spring cloud,mybatis,gradle

4.使用二次验证APP扫描二维码进行绑定,软件会自动生成验证码

使用Microsoft-Authenticator做系统登录的二次身份验证(双因素认证),Java实用工具,java,spring cloud,mybatis,gradle

 5.前端传递对应的验证码到后端,后端使用工具类中的方法校验,返回值为boolean类型

这样系统就将微软二次验证系统接入到你的系统里面啦文章来源地址https://www.toymoban.com/news/detail-638233.html

到了这里,关于使用Microsoft-Authenticator做系统登录的二次身份验证(双因素认证)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Streamlit应用程序使用Streamlit-Authenticator进行用户的安全身份验证实践(解决升级问题)

    Streamlit应用程序使用Streamlit-Authenticator进行用户的安全身份验证实践(解决升级问题)

    在Streamlit官方文档中,没有提供提供安全身份验证组件。目前,第三方streamlit-authenticator提供此功能,详见引用我原来的博文,在《Streamlit应用程序使用Streamlit-Authenticator进行用户的安全身份验证实践》文中,原使用的代码报错: 报错原因是Streamlit-Authenticator包升级了,原代码

    2024年02月06日
    浏览(8)
  • edge浏览器无法登录账号!Microsoft 帐户无法登录!

    edge浏览器无法登录账号!Microsoft 帐户无法登录!

    种种原因,将笔记本重置了,重新下载装了系统,但是麻烦也来了, Microsoft 帐户无法登录!edge浏览器无法登录账号 ,之前的保存的密码,加星的书签页同步不过去,这不完犊子了!干啥都要忘记密码,找回密码,重新设置密码,新密码不能与原密码重复……!忘记账号,重

    2024年02月08日
    浏览(42)
  • Edge浏览器无法登录Microsoft账户

    Edge浏览器无法登录Microsoft账户

    一直用 chrome浏览器,但是同步不了收藏,即便可以设置起来也比较麻烦。于是我就改用了Microsoft Edge浏览器,但在登录Microsoft 账户时,无法登录账号,一直卡在“请等候”这个页面。 网上查了下,解决的方法如下: 1、操作Edge浏览器 在控制面板或者IE浏览器中打开Internet选项

    2024年02月05日
    浏览(43)
  • Microsoft账户打不开登录界面、无法登录,但网络可正常上网,解决方法

    自己的情况: 实际上浏览器是可以正常上网的,不过windows网络那里显示“无网络连接”,且office等微软账号登录不上,登录界面都无法正常显示, 提示“没有网络”或类似的网络问题 。不是提示“发生了错误” 尝试了如下数遍,都不行: 参考自:https://www.zhihu.com/question

    2024年02月04日
    浏览(42)
  • Microsoft 365 - Teams无法登录的解决方案

    Microsoft 365 - Teams无法登录的解决方案

    在使用Teams时候,登录遇到下面错误 \\\"We\\\'re sorry - we\\\'ve run into an issue.\\\"   经过排查,可以通过下面方式解决: 1. 在上面错误弹框中点击Signing out退出登录; 2. 到路径”C:Users username AppDataRoamingMicrosoftTeams“下删除所有folder/file 3. 打开注册表,在路径”ComputerHKEY_LOCAL_MACHINESO

    2024年02月11日
    浏览(10)
  • Win11微软账号登录不上?Win11登录Microsoft账户出错的解决方法

    Win11微软账号登录不上?Win11登录Microsoft账户出错的解决方法

    Win11微软账号登录不上?近期有部分Win11用户反映在登录微软账号会出现一直转圈,无法登录的情况,这样导致部分功能都不能正常使用了,为此十分令人头疼。那么对于这一情况,有没有什么方法可以有效的解决呢?下面小编教给大家操作方法,大家可以去尝试看看。 更多

    2024年02月03日
    浏览(17)
  • Python解决微软Microsoft的登录机器人验证

    Python解决微软Microsoft的登录机器人验证

    前言 本文是该专栏的第8篇, 结合优质项目案例 , 让你精通使用Pyppeteer ,后面会持续分享Pyppeteer的干货知识,记得关注。 在注册微软Microsoft账号或者注册outlook邮箱账号的时候,会遇到如下机器人验证: 是的,你可能第一眼看到这个验证页面,首先会想到是定位它的页面元

    2023年04月21日
    浏览(48)
  • Win10改用microsoft账户登录发生了错误怎么解决?

    Win10改用microsoft账户登录发生了错误怎么解决?

    有朋友在账户设置里想要改用microsoft账户登录但是提示发生了错误,大家想知道什么原因,我来告诉大家,这其实是因为该本地账户是本机唯一管理员,导致和Microsoft账户有谜之冲突。解决方法是先新建一个普通账户,然后再改用microsoft账户登录。 Win10改用microsoft账户登录发

    2024年02月11日
    浏览(10)
  • 如何通过OAuth2.0完成Microsoft平台登录验证

    如何通过OAuth2.0完成Microsoft平台登录验证

    参考内容: OAuth2 in Python | TestDriven.io 代表用户获取访问权限 - Microsoft Graph | Microsoft Learn OAuth 2.0 Bearer Token Usage 首先需要了解的是,通过Microsoft平台做身份验证,有一些配置时拿到的参数不可或缺(在身份验证的步骤中会用到,不一定是同一个步骤用到),其中包括: client_id:

    2024年02月09日
    浏览(16)
  • Java接入Microsoft Azure AD实现SSO登录

    Java接入Microsoft Azure AD实现SSO登录

    应用程序通过单点登录解决账号创建问题 SSO(SingleSign-On,单点登录)通过在IDP(身份验证提供商)登录成功后,就可以访问IDP关联的应用程序以及相关权限 为了解决以下问题: 用户使用多个应用程序时,需要创建多个账号 如果用户在所有平台创建的账号密码一致,可能会

    2024年02月05日
    浏览(14)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包