内网实现Google Authenticator二步验证
程序员文章站
2022-07-10 13:43:10
0.概述相关背景参考https://blog.csdn.net/lizhengjava/article/details/76947962,本Demo将调用google api生成二维码改为了com.google.zxing包本地生成。1. pom.xml dependencies部分 commons-codec
0.概述
相关背景参考https://blog.csdn.net/lizhengjava/article/details/76947962,本Demo将调用google api生成二维码改为了com.google.zxing包本地生成。
1. pom.xml dependencies部分
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
2.GoogleAuthenticatorUtils.java
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* @author alexhu
* 主要功能:生成密钥、生成二维码内容、校验身份
* 依赖:
* <dependency>
* <groupId>commons-codec</groupId>
* <artifactId>commons-codec</artifactId>
* <version>1.14</version>
* </dependency>
*/
public class GoogleAuthenticatorUtils {
public static final int SECRET_SIZE = 10;
public static final String SEED = "g8GjEvTbW5oVSV7avLBdwIHqGlUYNzKFI7izOF8GwLDVKs2m0QN7vxRs2im5MDaNCWGmcD2rvcZx";
public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG";
/**
* default 3 - max 17 (from google docs)最多可偏移的时间
*/
int window_size = 3;
public void setWindowSize(int s) {
if (s >= 1 && s <= 17) {
window_size = s;
}
}
/**
* 验证身份验证码是否正确
*
* @param codes 输入的身份验证码
* @param savedSecret 密钥
* @return
*/
public static Boolean authcode(String codes, String savedSecret) {
long code = 0;
try {
code = Long.parseLong(codes);
} catch (Exception e) {
e.printStackTrace();
}
long t = System.currentTimeMillis();
GoogleAuthenticatorUtils ga = new GoogleAuthenticatorUtils();
// should give 5 * 30 seconds of grace...
ga.setWindowSize(ga.window_size);
return ga.check_code(savedSecret, code, t);
}
/**
* 获取密钥
*
* @param user 用户
* @param host 域
* @return 密钥
*/
public static String genSecret(String user, String host) {
String secret = GoogleAuthenticatorUtils.generateSecretKey();
GoogleAuthenticatorUtils.getQRBarcodeURL(user, host, secret);
return secret;
}
/**
* 生成密钥
*
* @return
*/
private static String generateSecretKey() {
SecureRandom sr = null;
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);
String encodedKey = new String(bEncodedKey);
return encodedKey;
} catch (NoSuchAlgorithmException e) {
// should never occur... configuration error
}
return null;
}
/**
* 获取二维码内容URL
*
* @param user 用户
* @param host 域
* @param secret 密钥
* @return 二维码URL
*/
public static String getQRBarcodeURL(String user, String host, String secret) {
String format = "otpauth://totp/%s@%s?secret=%s";
return String.format(format, user, host, secret);
}
/**
* 校验code是否正确
*
* @param secret 密钥
* @param code 动态code
* @param timeMsec 时间
* @return
*/
private 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) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
if (hash == code) {
return true;
}
}
return false;
}
/**
* 时间校验密钥与code是否匹配
*
* @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;
}
}
3.GenerateQRCodeUtils.java
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
/**
* @author alexhu
*
* 主要功能:根据二维码内容生成二维码,并保存在指定位置
*
* 依赖:
* <dependency>
* <groupId>com.google.zxing</groupId>
* <artifactId>core</artifactId>
* <version>3.4.1</version>
* </dependency>
*/
public class GenerateQRCodeUtils {
/**
* 二维码颜色
*/
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
/**
* 图片的宽度
*/
private static int WIDTH = 200;
/**
* 图片的高度
*/
private static int HEIGHT = 200;
/**
* 图片的格式
*/
private static String FORMAT = "png";
/**
* 生成二维码
*
* @param basePath 配置文件定义的生成二维码存放文件夹
* @param content 二维码内容
* @return 文件路径
*/
public static String generateQRCodeImg(String basePath, String content){
try {
Map<EncodeHintType, String> encodeMap = new HashMap<EncodeHintType, String>();
// 内容编码,生成二维码矩阵
encodeMap.put(EncodeHintType.CHARACTER_SET, "utf-8");
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, encodeMap);
File file = new File(basePath);
if (!file.exists() && !file.isDirectory()){
file.mkdirs();
}
//文件名,默认为时间为名
String filePath = basePath + System.currentTimeMillis() + "." + FORMAT;
File outputFile = new File(filePath);
if (!outputFile.exists()){
// 生成二维码文件
writeToFile(bitMatrix, FORMAT, outputFile);
}
return filePath;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 把二维码矩阵保存为文件
*
* @param matrix 二维码矩阵
* @param format 文件类型,这里为png
* @param file 文件句柄
* @throws IOException
*/
public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
BufferedImage image = toBufferedImage(matrix);
if (!ImageIO.write(image, format, file)) {
throw new IOException("Could not write an image of format " + format + " to " + file);
}
}
/**
* 生成二维码矩阵(内存)
*
* @param matrix 二维码矩阵
* @return
*/
public static BufferedImage toBufferedImage(BitMatrix matrix) {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
}
}
return image;
}
}
4.GoogleAuthenticatorTest.java
import org.junit.Test;
import static org.example.GenerateQRCodeUtils.generateQRCodeImg;
import static org.example.GoogleAuthenticatorUtils.*;
/**
* Unit test for Google Authenticator.
*/
public class GoogleAuthenticatorTest {
/**
* Rigorous Test :-)
*/
@Test
public void genTest() {
/*
* 注意:先运行前两步,获取密钥和二维码url。 然后只运行第三步,填写需要验证的验证码,和第一步生成的密钥
*/
String user = "testUser";
String host = "test.com";
// 第一步:获取密钥
String secret = genSecret(user, host);
System.out.println("secret:" + secret);
// 第二步:根据密钥获取二维码图片url(可忽略)
String url = getQRBarcodeURL(user, host, secret);
System.out.println("url:" + url);
// 第三步 生成二维码
generateQRCodeImg("", url);
}
@Test
public void verifyTest() {
// 第四步:验证(第一个参数是需要验证的验证码,第二个参数是第一步生成的secret运行)
boolean result = authcode("105938", "WUH2RO3Q4D53AF5Z");
System.out.println("result:" + result);
}
}
本文地址:https://blog.csdn.net/dgatiger/article/details/110196740
上一篇: python cs架构实现简单文件传输
推荐阅读
-
Google Authenticator(谷歌身份验证器)C#版
-
PHP设置谷歌验证器(Google Authenticator)实现操作二步验证
-
Laravel-google-authenticator--Google验证码
-
Android中新引进的Google Authenticator验证系统工作原理浅析
-
PHP版谷歌验证 (Google Authenticator)
-
内网实现Google Authenticator二步验证
-
vue使用Google Recaptcha验证的实现示例
-
time-based基于google key生成6位验证码(google authenticator)
-
PHP设置谷歌验证器(Google Authenticator)实现操作二步验证
-
Google Authenticator(谷歌身份验证器)C#版