原理
我们先看下别人的文章 Google账户两步验证的工作原理 来了解一下原理。
从上面的文章我们看来有2种算法, 1. HOTP, 2. TOTP, 我们都知道如果算法一样, 输入参数一样, 那么必然得到的结果是一样的。
客户端和服务器绑定的时候, 首先需要协商好一个密钥K, 该秘钥只有客户端和服务器知道。
HOTP是基于计数器的密码生成, 服务器和客户端都保存着一个相同的计数器, 当需要验证的时候, 客户端计算一次密码, 客户端的计数器+1, 提交验证的时候, 客户端计算后, 服务器的计数器+1, 这样就服务器和客户端的计数器保持一样。
TOTP是基于时间的密码生成, 服务器和客户端都根据时间来计算密码, 比较客户端提交的密码是否一致。
实战
网站登录二次校验。
流程
绑定阶段:
- 生成秘钥
- google authenticator绑定
登录阶段:
- 登录
- 判断账号密码是否正确
- 如果正确, 再使用二次校验
开发
- maven依赖
<!-- https://mvnrepository.com/artifact/com.warrenstrange/googleauth -->
<dependency>
<groupId>com.warrenstrange</groupId>
<artifactId>googleauth</artifactId>
<version>1.5.0</version>
</dependency>
- 模拟绑定
/**
* 绑定
*
*/
public static void bind(){
// 获取秘钥, 进行协商(使用预先生成好的)
GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
GoogleAuthenticatorKey credentials = googleAuthenticator.createCredentials();
System.out.println(credentials.getKey()); // 记录下来, 绑定设备后, 需要此key来做校验
// 生成绑定的url
GoogleAuthenticatorQRGenerator gAuthenQRGenerator = new GoogleAuthenticatorQRGenerator();
String otpAuthTotpURL = gAuthenQRGenerator.getOtpAuthTotpURL("itkaoti", "xiaodoubi", credentials);
System.out.println(otpAuthTotpURL);
}
public static void main(String[] args) {
bind();
}
结果:
M2IG4BPH6PWFQZKZZMA2MARPFEA7LDAF
otpauth://totp/itkaoti:xiaodoubi?secret=M2IG4BPH6PWFQZKZZMA2MARPFEA7LDAF&issuer=itkaoti&algorithm=SHA1&digits=6&period=30
记录上面的 M2IG4BPH6PWFQZKZZMA2MARPFEA7LDAF
秘钥, 后续验证需要, 下面的otpauth链接 直接使用在线二维码转换, 转成二维码
下面开始手机绑定, 我们需要先安装google Authenticator, 打开app 具体操作: 右上角三个点 --> 添加账户 --> 个人账户 --> 扫描QR码
扫描后我们发现多了一个itkaoti xiaodoubi的账号, 点开就可以看到一次性密码代码。
验证
public static void main(String[] args) {
verify();
}
public static void verify(){
String key = "M2IG4BPH6PWFQZKZZMA2MARPFEA7LDAF"; // 绑定步骤的生成的key
while(true){
System.out.println("请输入authenticator二次校验密码...");
String next = new Scanner(System.in).next();
GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
boolean authorize = googleAuthenticator.authorize(key, Integer.parseInt(next));
if(authorize){
System.out.println("校验成功");
}else{
System.out.println("校验失败");
}
}
}
结果:
请输入authenticator二次校验密码...
097158
校验成功
请输入authenticator二次校验密码...
123456
校验失败
请输入authenticator二次校验密码...
使用二次校验, 极大的增加了网站的安全性。
具体api讲解
GoogleAuthenticator
主要的类, 我们是它来生成了key, 以及校验
方法 | 说明 | |
---|---|---|
createCredentials() | 生成秘钥 | |
createCredentials(String userName) | 根据用户名生成秘钥 | |
getTotpPassword(String secret) | 根据秘钥获取验证码 | |
public int getTotpPassword(String secret, long time) | 根据秘钥获取验证码(指定时间) | |
getTotpPasswordOfUser(String userName) | 根据用户名获取验证码 | |
getTotpPasswordOfUser(String userName, long time) | 根据用户名获取验证码(指定时间) | |
authorize(String secret, int verificationCode) | 验证 | |
authorize(String secret, int verificationCode, long time) | 验证(指定时间) | |
authorizeUser(String userName, int verificationCode) | 验证 | |
authorizeUser(String userName, int verificationCode, long time) | 验证(指定时间) |
GoogleAuthenticatorConfig
private long timeStepSizeInMillis = TimeUnit.SECONDS.toMillis(30); // 验证码刷新时间
private int windowSize = 3;
private int codeDigits = 6; // 验证码个数
private int numberOfScratchCodes = 5;
private int keyModulus = (int) Math.pow(10, codeDigits);
private int secretBits = 160;
private KeyRepresentation keyRepresentation = KeyRepresentation.BASE32;
private HmacHashFunction hmacHashFunction = HmacHashFunction.HmacSHA1;