之前感觉双因子验证很高大上,今天正好有时间就学了学
目录
参考文档:快速接入Google两步认证Google Authenticator_最咔酷学院的博客-CSDN博客_谷歌认证
直接上代码
引入jar
实体类
package pers.wwz.study.twofactorauthentication.entity;
import lombok.Data;
@Data
public class Code {
/**
* 图片base64串
*/
private String imgBase64;
/**
* randomSecretKey
* 很重要
*/
private String randomSecretKey;
}
工具类
package pers.wwz.study.twofactorauthentication.utils;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import de.taimos.totp.TOTP;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;
import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.UUID;
/**
* @Author bilibili-nanoda
* @Date 2021/8/13 10:33
* @Version 1.0
*/
public class GoogleAuthenticationTool {
public static String generateSecretKey() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
Base32 base32 = new Base32();
return base32.encodeToString(bytes);
}
/**
* 根据32位随机码获得正确的6位数字
*
* @param secretKey
* @return
*/
public static String getTOTPCode(String secretKey) {
Base32 base32 = new Base32();
byte[] bytes = base32.decode(secretKey);
String hexKey = Hex.encodeHexString(bytes);
return TOTP.getOTP(hexKey);
}
/**
* 生成绑定二维码(字符串)
*
* @param account 账户信息(展示在Google Authenticator App中的)
* @param secretKey 密钥
* @param title 标题 (展示在Google Authenticator App中的)
* @return
*/
public static String spawnScanQRString(String account, String secretKey, String title) {
try {
return "otpauth://totp/"
+ URLEncoder.encode(title + ":" + account, "UTF-8").replace("+", "%20")
+ "?secret=" + URLEncoder.encode(secretKey, "UTF-8").replace("+", "%20")
+ "&issuer=" + URLEncoder.encode(title, "UTF-8").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
/**
* 生成二维码(文件)【返回图片的base64,若指定输出路径则同步输出到文件中】
*
* @param barCodeData 二维码字符串信息
* @param outPath 输出地址
* @param height
* @param width
* @throws WriterException
* @throws IOException
*/
public static String createQRCode(String barCodeData, String outPath, int height, int width)
throws WriterException, IOException {
BitMatrix matrix = new MultiFormatWriter().encode(barCodeData, BarcodeFormat.QR_CODE,
width, height);
BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(matrix);
ByteArrayOutputStream bof = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", bof);
String base64 = imageToBase64(bof.toByteArray());
if(outPath!=null&&!outPath.equals("")) {
try (FileOutputStream out = new FileOutputStream(outPath)) {
MatrixToImageWriter.writeToStream(matrix, "png", out);
}
}
return base64;
}
/**
* 将图片文件转换成base64字符串,参数为该图片的路径
*
* @param dataBytes
* @return java.lang.String
*/
private static String imageToBase64(byte[] dataBytes) {
// 对字节数组Base64编码
BASE64Encoder encoder = new BASE64Encoder();
if (dataBytes != null) {
return "data:image/jpeg;base64," + encoder.encode(dataBytes);// 返回Base64编码过的字节数组字符串
}
return null;
}
public static void main(String[] args) {
String secretKey = UUID.randomUUID().toString().replace("-", "");
for (int i = 0; i < 100; i++) {
String totpCode = getTOTPCode(secretKey);
System.out.println(totpCode);
}
}
}
控制器
package pers.wwz.study.twofactorauthentication;
import com.google.zxing.WriterException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import pers.wwz.study.twofactorauthentication.entity.Code;
import pers.wwz.study.twofactorauthentication.utils.GoogleAuthenticationTool;
import java.io.IOException;
@Slf4j
@RestController
@RequestMapping("/code")
public class CodeController {
/**
* 生成绑定的二维码(base64),并把密钥返回
* @param userId
* @return
*/
@GetMapping("/genCode")
public Code genCode(@RequestParam("userId")String userId) {
// 这个很重要 和用户相关
String randomSecretKey = GoogleAuthenticationTool.generateSecretKey();
//此步设置的参数就是App扫码后展示出来的参数
// 扫描后展示内容格式:titl(userId),如wangdachui(123456)
String qrCodeString = GoogleAuthenticationTool.spawnScanQRString(userId, randomSecretKey, "wangdachui");
String qrCodeImageBase64 = null;
try {
qrCodeImageBase64 = GoogleAuthenticationTool.createQRCode(qrCodeString, null, 512, 512);
} catch (WriterException | IOException e) {
e.printStackTrace();
}
log.info("随机密钥:{}",randomSecretKey);
Code code = new Code();
code.setImgBase64(qrCodeImageBase64);
code.setRandomSecretKey(randomSecretKey);
return code;
}
/**
* 执行谷歌两步验证绑定
* 输入绑定二维码上的6位数字
* 个人认为randomSecretKey不输入也可以,直接生成的时候存起来(扫码可以获取randomSecretKey)
* @return
*/
@PostMapping("/bindCode")
@ResponseBody
public String bindCode(@RequestParam("randomSecretKey")String randomSecretKey,@RequestParam("inputGoogleCode")String inputGoogleCode){
String rightCode =GoogleAuthenticationTool.getTOTPCode(randomSecretKey);
if(rightCode.equals(inputGoogleCode)){
return "成功";
}
return "失败";
}
/**
* 登录验证
* @return
*/
@PostMapping("/loginVerify")
@ResponseBody
public String loginVerify(@RequestParam String code){
String rightCode =GoogleAuthenticationTool.getTOTPCode("KFCTNPLXJ7COTL5BTSSK76BLUWEITHHY");
if(rightCode.equals(code)){
return "成功";
}
return "失败";
}
}