最近才知道公司还在做国外的业务,要实现一个登陆辅助验证系统。咱们国内是用手机短信做验证,当然 这个google身份验证只是一个辅助验证登陆方式。看一下演示
看到了嘛。 手机下载一个谷歌身份验证器就可以 。
谷歌身份验证器,我本身是一个基于时间做加密计算然后得出相同结果 本身很简单。
下边在网上查的 可以做一下了解:谷歌身份验证就是基于TOTP算法
TOTP算法,全称为“Time-based One-time Password algorithm”,中文译为基于时间的一次性密码算法。它是一种从共享密钥和当前时间计算一次性密码的算法,已被采纳为Internet工程任务组标准RFC 6238。TOTP是开放身份验证计划(OATH)的基石,并被用于许多双因素身份验证系统。详细的可以百度一下搜索原理,我们这里只是介绍一下使用。
下边是代码 :
import org.apache.commons.codec.binary.Base32; import org.apache.commons.codec.binary.Hex; import org.springframework.util.StringUtils; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; public class GoogleAuthenticator { /** * 时间前后偏移量 目的解决30秒内有计算有误差不一致的发生 */ private static int WINDOW_SIZE = 0; /** * 加密方式,HmacSHA1、HmacSHA256、HmacSHA512 */ private static final String CRYPTO = "HmacSHA1"; /** * 生成二维码内容 * * @param secretKey 密钥 * @param account 账户名 * @param issuer 网站地址(可不写) * @return */ public static String getQrCodeText(String secretKey, String account, String issuer) { String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase(); try { return "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")) : ""); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } /** * 检验 code 是否正确 * * @param secret 密钥 * @param code code * @param time 时间戳 * @return */ public static boolean checkCode(String secret, long code, long time) { 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) { return false; } if (hash == code) { return true; } } return false; } /** * 根据时间偏移量计算 * * @param key * @param t * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException */ private static long verifyCode(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, 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; } public static String getkeyBase32() { // 生成一个随机的密钥字节数组 SecureRandom random = new SecureRandom(); byte[] keyBytes = new byte[20]; // 一般长度为16、20或32字节 random.nextBytes(keyBytes); // 将密钥转换成Base32格式以便用户显示或扫描二维码 Base32 base32 = new Base32(); String secretKeyBase32 = base32.encodeToString(keyBytes); return secretKeyBase32; } public static void main(String[] args) { // String secretKeyBase32 = getkeyBase32(); String secretKeyBase32 = "YR3TEMNWNWOVMFPFK3BB2SLM2P3IV6MF"; System.out.println("加密信息》》》" + secretKeyBase32); System.out.println("拿到这个字符串 二维码工具:去生成二维码图片就可以了" + getQrCodeText(secretKeyBase32, "jxd", "hdjz")); System.out.println(checkCode(secretKeyBase32, Long.parseLong("034944"), System.currentTimeMillis())); } }
我这个方法,都是基于现成的,不需要额外引入j2totp 等类库 很方便可以拿去用