集成Google Authenticator实现多因素认证(MFA)

2024-06-03 08:52

本文主要是介绍集成Google Authenticator实现多因素认证(MFA),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 参考
  • 1、应用背景
  • 2、多因素认证
  • 3、谷歌google authenticator集成用法
    • 3.1、原理
    • 3.2、 MFA绑定
      • 3.2.1、 用户输入用户名密码登录
      • 3.2.2、检查是否已经绑定MFA(检查数据库是否保存该用户的google secret)
      • 3.2.3、谷歌身份证认证器扫描绑定
      • 3.2.4、手动测试验证码
    • 3.3、基于OTP技术的MFA认证原理如下:
    • 3.4、关键代码
      • 3.4.1、调用LoginService的login方法进行登录(需传入手机上显示的6位动态验证码,否则校验不被通过)
      • 3.4.2、GoogleAuthenticatorUtils提供了生成密钥、校验验证码是否和密钥匹配等功能
  • 4、JumpServer 常用的 MFA 工具
    • 安卓版:
    • IOS版:
    • 微信小程序:
    • 商业产品:

参考

设计一个安全性架构时
手把手教你集成Google Authenticator实现多因素认证(MFA)
身份认证之多因素认证方法
谷歌身份验证器的正确使用方法
JupmServer堡垒机之MFA知识点说明

1、应用背景

多因素认证(Multi Factor Authentication,简称 MFA)的使用背景主要出于对账户和系统安全性的增强需求。传统的用户名和密码认证方式在面对日益复杂的网络威胁时显得不够安全,因为密码可能被泄露、猜测或被暴力破解。随着攻击者拥有越来越先进的工具,对更安全的认证协议的需求正在加大。多因素认证通过引入额外的认证因素,提供了更强大的安全层级。同时,将“实体所有”、“实体特征”、 “实体所知”三种不同认证因素结合起来增强系统或设备的安全性是研究人员容易设想的方向,因此多因素认证解决认证安全问题是大势所趋。

2、多因素认证

多因素认证是一种简单有效的安全实践方法,旨在提供两层或更多的身份验证保护。最常见的三种验证因素包括知识(实体所知)、持有物(实体所有)和固有属性(实体特征)。知识因素指的是用户知道的信息,如安全密保问题或个人识别号码如pin码;持有物因素是用户拥有的物品,例如SMS密码或硬件令牌;固有属性则与用户的身体特征有关,如指纹或面部识别等。这种方法提高了安全性,因为即使某个因素被泄露或破解,攻击者仍需要其他因素才能访问用户账户或系统。尤其是在面临日益复杂的网络安全威胁时,MFA的实施可以有效减少未经授权的访问,提高账户安全性。
三种验证因素可以总结如下:
基于实体所知的方法是最为广泛使用的方法,如密码、验证码等,其成本低、实现简单,但同时也面临较大安全威胁如暴力破解和木马侵入等。

基于实体所有的方法是安全性较高、成本较高的一类,主要应用在IC卡、门禁卡和数字签名等。但缺点是基于实体所有的方法因为存在固体实物,因此会面临着损坏和被复制的风险。

基于实体特征的方法是安全性最高的一种方式,通常采用生物识别方法进行验证。主要应用在指纹、虹膜、声波特征等验证方式。实体特性鉴别的准确性和效率主要取决于开发过程中的算法特征。

3、谷歌google authenticator集成用法

3.1、原理

基于OTP技术的MFA认证,是指在传统的用户名密码认证的基础上,增加一个额外的OTP认证。OTP是指一次性密码,每个OTP只能使用一次,有效期通常为30秒。

3.2、 MFA绑定

3.2.1、 用户输入用户名密码登录

在这里插入图片描述

3.2.2、检查是否已经绑定MFA(检查数据库是否保存该用户的google secret)

用户名密码登录成功后,检查是否已经绑定MFA(检查数据库是否保存该用户的google secret)
如果未绑定,则需要绑定MFA进行二次认证。
1、调用生产string secretKey = GoogleAuthenticatorUtils.createSecretKey(); 并入库;
2、利用谷歌库生成绑定二维码String keyUri = GoogleAuthenticatorUtils.createKeyUri(secretKey, username, “Demo_System”); // Demo_System 服务标识不参与运算,可任意设置
在这里插入图片描述

3.2.3、谷歌身份证认证器扫描绑定

前端弹出二维码,用户拿移动端打开谷歌身份证认证器app点击扫描绑定
在这里插入图片描述

3.2.4、手动测试验证码

在这里插入图片描述

3.3、基于OTP技术的MFA认证原理如下:

  • 用户在登录时,首先输入用户名和密码。
  • 服务器验证用户名和密码是否正确。
  • 如果用户名和密码正确,前端提示输入MFA验证码。
  • 用户在绑定认证过的设备上打开谷歌身份认证器查看当前时间点生成的OTP。
  • 用户输入OTP,点击确定
  • 服务器验证OTP是否正确。boolean verification = GoogleAuthenticatorUtils.verification(user.getGoogleAuthenticatorSecret(), loginParam.getMfaCode());
  • 如果OTP正确,服务器允许用户登录。

3.4、关键代码

3.4.1、调用LoginService的login方法进行登录(需传入手机上显示的6位动态验证码,否则校验不被通过)

以下分享一次Spring Boot集成Goole Authenticator的案例关键性代码

@Service
public class LoginServiceImpl implements LoginService {private final UserService userService;public LoginServiceImpl(UserService userService) {this.userService = userService;}/*** 登录接口* @param loginParam {"username":"用户名", "password":"密码", "mfaCode":"手机应用Google Authenticator生成的验证码"}* @param servletRequest* @return*/@Overridepublic UserVo login(LoginParam loginParam, HttpServletRequest servletRequest) {// 校验用户名和密码是否匹配User user = getUserWithValidatePass(loginParam.getUsername(), loginParam.getPassword());// 验证数据库保存的密钥和输入的验证码是否匹配boolean verification = GoogleAuthenticatorUtils.verification(user.getGoogleAuthenticatorSecret(), loginParam.getMfaCode());if (!verification) {throw new BadRequestException("验证码校验失败");}// 用户信息保存到session中servletRequest.getSession().setAttribute("user", user);UserVo userVo = new UserVo();BeanUtils.copyProperties(user, userVo);return userVo;}/*** 生成二维码的Base64编码* 可以使用手机应用Google Authenticator来扫描二维码进行绑定* @param username* @param password* @return*/@Overridepublic String generateGoogleAuthQRCode(String username, String password) {// 校验用户名和密码是否匹配User user = getUserWithValidatePass(username, password);String secretKey;if (StringUtils.isEmpty(user.getGoogleAuthenticatorSecret())) {secretKey = GoogleAuthenticatorUtils.createSecretKey();}else {secretKey = user.getGoogleAuthenticatorSecret();}// 生成二维码String qrStr;try(ByteArrayOutputStream bos = new ByteArrayOutputStream()){String keyUri = GoogleAuthenticatorUtils.createKeyUri(secretKey, username, "Demo_System");  // Demo_System 服务标识不参与运算,可任意设置QRCodeUtils.writeToStream(keyUri, bos);qrStr = Base64.encodeBase64String(bos.toByteArray());}catch (WriterException | IOException e) {throw new ServiceException("生成二维码失败", e);}if (StringUtils.isEmpty(qrStr)) {throw new ServiceException("生成二维码失败");}user.setGoogleAuthenticatorSecret(secretKey);userService.updateById(user);return "data:image/png;base64," + qrStr;}private User getUserWithValidatePass(String username, String password) {String mismatchTip = "用户名或者密码不正确";// 根据用户名查询用户信息User user = userService.getByUsername(username).orElseThrow(() -> new BadRequestException(mismatchTip));// 比对密码是否正确String encryptPassword = SecureUtil.md5(password);if (!encryptPassword.equals(user.getPassword())) {throw new BadRequestException(mismatchTip);}return user;}
}

3.4.2、GoogleAuthenticatorUtils提供了生成密钥、校验验证码是否和密钥匹配等功能

public class GoogleAuthenticatorUtils {/*** 时间前后偏移量* 用于防止客户端时间不精确导致生成的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 final int TIME_OFFSET = 0;/*** 创建密钥*/public static String createSecretKey() {SecureRandom random = new SecureRandom();byte[] bytes = new byte[20];random.nextBytes(bytes);return Base32.encode(bytes).toLowerCase();}/*** 根据密钥获取验证码* 返回字符串是因为数值有可能以0开头* @param secretKey 密钥* @param time 第几个30秒 System.currentTimeMillis() / 1000 / 30*/public static String generateTOTP(String secretKey, long time) {byte[] bytes =  Base32.decode(secretKey.toUpperCase());String hexKey =HexUtil.encodeHexStr(bytes);String hexTime = Long.toHexString(time);return TOTP.generateTOTP(hexKey, hexTime, "6");}/*** 生成 Google Authenticator Key Uri* Google Authenticator 规定的 Key Uri 格式: otpauth://totp/{issuer}:{account}?secret={secret}&issuer={issuer}* https://github.com/google/google-authenticator/wiki/Key-Uri-Format* 参数需要进行 url 编码 +号需要替换成%20* @param secret 密钥 使用 createSecretKey 方法生成* @param account 用户账户 如: example@domain.com* @param issuer 服务名称 如: Google,GitHub* @throws UnsupportedEncodingException*/@SneakyThrowspublic static String createKeyUri(String secret, String account, String issuer) throws UnsupportedEncodingException {String qrCodeStr = "otpauth://totp/${issuer}:${account}?secret=${secret}&issuer=${issuer}";ImmutableMap.Builder<String, String> mapBuilder = ImmutableMap.builder();mapBuilder.put("account", URLEncoder.encode(account, "UTF-8").replace("+", "%20"));mapBuilder.put("secret", URLEncoder.encode(secret, "UTF-8").replace("+", "%20"));mapBuilder.put("issuer", URLEncoder.encode(issuer, "UTF-8").replace("+", "%20"));return StringSubstitutor.replace(qrCodeStr, mapBuilder.build());}/*** 校验方法** @param secretKey 密钥* @param totpCode TOTP 一次性密码* @return 验证结果*/public static boolean verification(String secretKey, String totpCode) {long time = System.currentTimeMillis() / 1000 / 30;// 优先计算当前时间,然后再计算偏移量,因为大部分情况下客户端与服务的时间一致if (totpCode.equals(generateTOTP(secretKey, time))) {return true;}for (int i = -TIME_OFFSET; i <= TIME_OFFSET; i++) {// i == 0 的情况已经算过if (i != 0) {if (totpCode.equals(generateTOTP(secretKey, time + i))) {return true;}}}return false;}}

4、JumpServer 常用的 MFA 工具

安卓版:

  • google authenticator
  • microsoft authenticator
  • 阿里云 App 虚拟 MFA
  • CKEY 令牌

IOS版:

  • google authenticator
  • microsoft authenticator
  • 阿里云 app 虚拟 MFA

微信小程序:

  • MinaOTP
  • MFA Authentication
  • CKEY 令牌

商业产品:

  • 宁盾

这篇关于集成Google Authenticator实现多因素认证(MFA)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1026522

相关文章

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合