双因子认证,TOTP动态口令认证

365网络股份有限公司总部 📅 2025-08-13 17:43:14 ✍️ admin 👁️ 3896 ❤️ 664
双因子认证,TOTP动态口令认证

介绍

TOTP:Time-based One-Time Password写,基于对称密钥与时间戳算法的一次性认证码。 时间同步,基于客户端的动态口令和动态口令验证服务器的时间比对,默认每30秒产生一个新口令,要求客户端和服务器能够十分精确的保持正确的时钟,客户端和服务端基于时间计算的动态口令才能一致。

算法安全的核心在于密钥 , 每个人通过对应账户生成的密钥是不同的 . 当他们用同一个算法加密时 , 会生成不同的随机密码,认证时客户端需要使用阿里身份宝,Goole 身份验证器等工具生成认证码。

totp认证java代码实现

public class OptUtil {

private OptUtil() {}

/** 生成32位的otp密钥 */

@SneakyThrows

public static String generateSecretKey() {

SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");

sr.setSeed(Base64.decodeBase64("b5rpa6kcdux2fpq0tkrt2wwsqeqmho0d"));

byte[] buffer = sr.generateSeed(20);

Base32 codec = new Base32();

return codec.encodeToString(buffer);

}

/** 校验 opt code */

public static boolean checkCode(String secret, int code) {

Base32 codec = new Base32();

byte[] decodedKey = codec.decode(secret);

long nowInSeconds = System.currentTimeMillis() / 1000L;

//考虑到网络延迟,预留1秒钟容错

for (int i = 0; i < 2; i++) {

long time = (nowInSeconds - i) / 30L;

int hash = verifyCode(decodedKey, time);

if (hash == code) {

return true;

}

}

//Opt校验不通过

return false;

}

@SneakyThrows

private static int verifyCode(byte[] key, long t) {

byte[] data = new byte[8];

long value = t;

for (int i = 8; i-- > 0; value >>>= 8) {

data[i] = (byte) value;

}

final String algorithm = "HmacSHA1";

SecretKeySpec signKey = new SecretKeySpec(key, algorithm);

Mac mac = Mac.getInstance(algorithm);

mac.init(signKey);

byte[] hash = mac.doFinal(data);

int offset = hash[20 - 1] & 0xF;

long truncatedHash = 0;

for (int i = 0; i < 4; ++i) {

truncatedHash <<= 8;

truncatedHash |= (hash[offset + i] & 0xFF);

}

truncatedHash &= 0x7FFFFFFF;

truncatedHash %= 1000000;

return (int) truncatedHash;

}

}

生成totp密钥绑定二维码

添加依赖

com.google.zxing

core

3.3.3

二维码生成代码

/**

* @description 生成二维码工具

*/

public class QrCodeUtil {

private QrCodeUtil() { }

/**

* @description: 生成一个普通的黑白二维码

* @param content 二维码内容

* @param width 生成图片矿都

* @param height 生成图片高度

* @return 二维码图片字节流

**/

public static byte[] drawQrCode(String content, int width, int height) throws WriterException, IOException {

MultiFormatWriter multiFormatWriter = new MultiFormatWriter();

BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, new HashMap<>() {

private static final long serialVersionUID = 1L;

{

put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

put(EncodeHintType.CHARACTER_SET, "UTF-8");

put(EncodeHintType.MARGIN, 0);

}

});

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

// 开始利用二维码数据图片,分别设为黑(0xFFFFFFFF)白(0xFF000000)两色

for (int x = 0; x < width; x++) {

for (int y = 0; y < height; y++) {

image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);

}

}

image.flush();

//分配13Kb

ByteArrayOutputStream outputStream = new ByteArrayOutputStream(13312);

ImageIO.write(image, "png", outputStream);

return outputStream.toByteArray();

}

}

新增接口生成base64绑定二维码图片数据

/** 生成密钥绑定二维码 */

@GetMapping(path = "/img/zx")

@SneakyThrows

public String keyZxImg(){

String key = OptUtil.generateSecretKey();

final String QRCODE_TEMPLATE = "otpauth://totp/xxx:{0}?secret={1}&issuer={2}";

String content = MessageFormat.format(QRCODE_TEMPLATE, "user@app", key, "DAS_TOTP");

byte[] contents = QrCodeUtil.drawQrCode(content, 320, 320);

return Base64.getEncoder().encodeToString(contents);

}

客户端使用类似阿里身份宝等工具扫描“密钥绑定二维码”接口生成的base64编码图片即可绑定密钥

totp认证码校验

/** 使用opt验证码登录 */

@GetMapping(path = "/check")

public boolean login(@RequestParam int optCode){

return OptUtil.checkCode(key_cache, optCode);

}

相关推荐

直播象棋的平台有哪些(如何选择最适合你的象棋直播平台)
如何开启微信运动功能
美国买电脑省钱指南:最佳选择与优惠