目录
应用场景:通过google authenticator 生成验证码进行登录
原理:本文不再陈述,参考网上搜索结果
过程:定义种子秘钥-->生成私钥-->根据私钥到app应用上获取验证--->校验验证码
app: ios 和andriod的app会有一定差异,网上可以找到,ios可以到app store搜索获取
1.定义种子秘钥
种子秘钥根据项目自己定义一个高复杂度的秘钥
代码中这段
public static final String SEED = "F583513762484929976ED25B18FDE6B4";
以下工具完成:定义种子秘钥-->生成私钥
@Slf4j public class GoogleAuthenticatorUtils { public static final int SECRET_SIZE = 10;// 来自谷歌文档,不用修改 public static final String SEED = "F583513762484929976ED25B18FDE6B4";// 产生密钥的种子 种子秘钥一个 私钥可以生成多个,main方法中 public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG";// 安全哈希算法(Secure Hash Algorithm) private static Integer window_size = 3;//可偏移的时间 -- 3*30秒的验证时间(客户端30秒生成一次验证码) /** * 生成密钥 * * @return */ public static String generateSecretKey() { SecureRandom sr; try { sr = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM); sr.setSeed(Base64.decodeBase64(SEED)); byte[] buffer = sr.generateSeed(SECRET_SIZE); Base32 codec = new Base32(); byte[] bEncodedKey = codec.encode(buffer); return new String(bEncodedKey); } catch (Exception e) { log.error("generateSecretKey:" + e.getMessage(), e); } return null; } /** * 校验验证码 * * @param secret * @param code * @param timeMsec * @return */ public static Boolean check_code(String secret, long code, long timeMsec) { Base32 codec = new Base32(); byte[] decodedKey = codec.decode(secret); long t = (timeMsec / 1000L) / 30L; for (int i = -window_size; i <= window_size; ++i) { long hash; try { hash = verify_code(decodedKey, t + i); } catch (Exception e) { log.error(e.getMessage(), e); throw new RuntimeException("校验异常"); } if (hash == code) { return true; } } return false; } /** * 生成验证码 * * @param key * @param t * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException */ private static int verify_code(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException { 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, "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); 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; } public static void main(String[] args) { String secretKey = generateSecretKey(); System.out.println(secretKey); Boolean aBoolean = check_code("KISNJ4O2OMHKB73F", Long.valueOf("943952"), System.currentTimeMillis()); System.out.println(aBoolean); } }
2.生成私钥
String secretKey = generateSecretKey(); 生成私钥 ,生成的私钥配置到app中,名称随便,秘钥填生成的,实际使用中你有可能使用接口api或者扫描方式来调用接口获取
3.根据私钥到app应用上获取验证
生成的私钥配置到app中,名称随便,秘钥填生成的,即可立即获取
4.校验验证码
登录校验则是这段核心,取用户配置的私钥+传递过来的6位验证码
check_code("KISNJ4O2OMHKB73F", Long.valueOf("943952"), System.currentTimeMillis());
关于私钥,二维码生成可通过在线二维码生成工具获取,私钥配置到数据库中用于后续登录校验使用(即main方法中实际校验结果)
网上搜索一堆:在线二维码生成工具 生成下载即可;代码方面的可参考搜其他相关二维码生成工具代码