Google Authenticator的Java示例

Google身份验证器Google Authenticator是谷歌推出的基于时间的一次性密码(Time-based One-time Password,简称TOTP),只需要在手机上安装该APP,就可以生成一个随着时间变化的一次性密码,用于帐户验证。

相对于手机短信两步验证(Two-step verification),Google Authenticator两步验证有两大好处:一是避免无法接收验证短信的痛苦,尤其是用国内手机接收国外短信,你会发现经常收不到;二是Google Authenticator不用联网也可用,手机欠费停机不影响。

国内外各大网站和应用都基本上支持Google Authenticator两步验证了。这篇文章就简单说明一下,如何在自己的网站上集成Google身份验证器

一、准备工作

需要jar包:commons-codec-1.8

二、Java类

1package controller; 2 3import java.security.InvalidKeyException; 4import java.security.NoSuchAlgorithmException; 5import java.security.SecureRandom; 6 7import javax.crypto.Mac; 8import javax.crypto.spec.SecretKeySpec; 9 10import org.apache.commons.codec.binary.Base32; 11import org.apache.commons.codec.binary.Base64; 12 13public class GoogleAuthenticatorSample { 14 15 public static final int SECRET_SIZE = 10; 16 17 public static final String SEED = "g8GjEvTbW5oVSV7avLBdwIHqGlUYNzKFI7izOF8GwLDVKs2m0QN7vxRs2im5MDaNCWGmcD2rvcZx"; 18 19 public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG"; 20 21 int window_size = 3; // default 3 - max 17 (from google docs)最多可偏移的时间 22 23 public void setWindowSize(int s) { 24 if (s >= 1 && s <= 17) 25 window_size = s; 26 } 27 28 /** 29 * 验证身份验证码是否正确 30 * 31 * @param codes 32 * 输入的身份验证码 33 * @param savedSecret 34 * 密钥 35 * @return 36 */ 37 public static Boolean authcode(String codes, String savedSecret) { 38 long code = 0; 39 try { 40 code = Long.parseLong(codes); 41 } catch (Exception e) { 42 e.printStackTrace(); 43 } 44 long t = System.currentTimeMillis(); 45 GoogleAuthenticatorSample ga = new GoogleAuthenticatorSample(); 46 ga.setWindowSize(3); // should give 5 * 30 seconds of grace... 47 boolean r = ga.check_code(savedSecret, code, t); 48 return r; 49 } 50 51 /** 52 * 获取密钥 53 * 54 * @param user 55 * 用户 56 * @param host 57 * 域 58 * @return 密钥 59 */ 60 public static String genSecret(String user, String host) { 61 String secret = GoogleAuthenticatorSample.generateSecretKey(); 62 GoogleAuthenticatorSample.getQRBarcodeURL(user, host, secret); 63 return secret; 64 } 65 66 private static String generateSecretKey() { 67 SecureRandom sr = null; 68 try { 69 sr = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM); 70 sr.setSeed(Base64.decodeBase64(SEED)); 71 byte[] buffer = sr.generateSeed(SECRET_SIZE); 72 Base32 codec = new Base32(); 73 byte[] bEncodedKey = codec.encode(buffer); 74 String encodedKey = new String(bEncodedKey); 75 return encodedKey; 76 } catch (NoSuchAlgorithmException e) { 77 // should never occur... configuration error 78 } 79 return null; 80 } 81 82 /** 83 * 获取二维码图片URL 84 * 85 * @param user 86 * 用户 87 * @param host 88 * 域 89 * @param secret 90 * 密钥 91 * @return 二维码URL 92 */ 93 public static String getQRBarcodeURL(String user, String host, String secret) { 94 String format = "https://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s%%3Fsecret%%3D%s"; 95 return String.format(format, user, host, secret); 96 } 97 98 private boolean check_code(String secret, long code, long timeMsec) { 99 Base32 codec = new Base32(); 100 byte[] decodedKey = codec.decode(secret); 101 long t = (timeMsec / 1000L) / 30L; 102 for (int i = -window_size; i <= window_size; ++i) { 103 long hash; 104 try { 105 hash = verify_code(decodedKey, t + i); 106 } catch (Exception e) { 107 e.printStackTrace(); 108 throw new RuntimeException(e.getMessage()); 109 } 110 if (hash == code) { 111 return true; 112 } 113 } 114 return false; 115 } 116 117 private static int verify_code(byte[] key, long t) 118 throws NoSuchAlgorithmException, InvalidKeyException { 119 byte[] data = new byte[8]; 120 long value = t; 121 for (int i = 8; i-- > 0; value >>>= 8) { 122 data[i] = (byte) value; 123 } 124 SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1"); 125 Mac mac = Mac.getInstance("HmacSHA1"); 126 mac.init(signKey); 127 byte[] hash = mac.doFinal(data); 128 int offset = hash[20 - 1] & 0xF; 129 long truncatedHash = 0; 130 for (int i = 0; i < 4; ++i) { 131 truncatedHash <<= 8; 132 truncatedHash |= (hash[offset + i] & 0xFF); 133 } 134 truncatedHash &= 0x7FFFFFFF; 135 truncatedHash %= 1000000; 136 return (int) truncatedHash; 137 } 138 139 public static void main(String[] args) { 140 /* 141 * 注意:先运行前两步,获取密钥和二维码url。 然后只运行第三步,填写需要验证的验证码,和第一步生成的密钥 142 */ 143 String user = "testUser"; 144 String host = "testHost"; 145 // 第一步:获取密钥 146 String secret = genSecret(user, host); 147 System.out.println("secret:" + secret); 148 // 第二步:根据密钥获取二维码图片url(可忽略) 149 String url = getQRBarcodeURL(user, host, secret); 150 System.out.println("url:" + url); 151 // 第三步:验证(第一个参数是需要验证的验证码,第二个参数是第一步生成的secret运行) 152 boolean result = authcode("271239", "OHXU6PLMZMDJIDY6"); 153 System.out.println("result:" + result); 154 } 155}

账户为:user@host。上面代码对应的账户为:testUser@testHost。
密钥是第一步生成。选择基于时间。

添加完成后,即可以动态的生成6位数字的验证码。
在项目需要做验证的地方,调用authcode()方法,传入用户输入的验证码和密钥,即可判断用户输入是否正确。

点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前

spring整合Google Kaptcha验证码的使用

Kaptcha是什么?kaptcha是谷歌开源的非常实用的验证码生成工具,基于SimpleCaptcha的开源项目。使用Kaptcha生成验证码十分简单并且参数可以进行自定义。只需添加jar包配置下就可以使用,通过配置,可以自己定义验证码大小、颜色、显示的字符等等。下面就来讲一下如何使用kaptcha生成验证码以及在服务器端取

PAM和账户安全配置

PAM(可插入认证模块)是UNIX系统上一个实现模块化的身份验证模块服务当程序需要对用户进行身份验证时加载并执行的。PAM文件通常位于/etc/pam.d目录中。配置文件/etc/pam.d/passwordauth/etc/pam.d/systemauth/etc/security/pwquality.conf配置密码创建要

JWT 认证方案学习

定义JWT(JSONWEBTOKEN)是一种安全通讯标准,它定义了一种紧凑自包含的方式,用于在各方之间安全的传输JSON对象。常见应用场景是API之间的认证通讯。一般的用户认证流程1、请求方发送账户密码到服务器,验证账户可用性。2、验证成功,生成session,保存在服务端。3、服务端返回一个s

APP Inventor 基于网络微服务器的即时通信APP

APPInventor基于网络微服务器的即时通信APP一、总结一句话总结:(超低配版的QQ,逃~)1、APPInventor是什么?google傻瓜式编程手机appAppInventor是一款谷歌公