pom文件
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<!-- Google Zxing 二维码 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.0</version>
</dependency>
GoogleGenerator
package com.xiaoq.galaxy.util;
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;
public class GoogleGenerator {
// 生成的key长度( Generate secret key length)
public static final int SECRET_SIZE = 10;
public static final String SEED = "22150146801713967E8g";
// Java实现随机数算法
public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG";
// 最多可偏移的时间
int window_size = 3; // default 3 - max 17
public static String generateSecretKey() {
SecureRandom sr;
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);
return new String(bEncodedKey);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* 这个format不可以修改,身份验证器无法识别二维码
*/
public static String getQRBarcode(String user, String secret) {
String format = "otpauth://totp/%s?secret=%s";
return String.format(format, user, secret);
}
/**
* 根据user和secret生成二维码的密钥
*/
public static String getQRBarcodeURL(String user, String host, String secret) {
String format = "http://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s?secret=%s";
return String.format(format, user, host, secret);
}
public boolean check_code(String secret, String 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());
}
System.out.println("code=" + code);
System.out.println("hash=" + hash);
if (code.equals(addZero(hash))) {
return true;
}
}
return false;
}
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;
}
private String addZero(long code) {
return String.format("%06d", code);
}
}
QRCodeUtil
package com.xiaoq.galaxy.util;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.util.HashMap;
import java.util.Map;
/**
* 二维码工具类
*/
public class QRCodeUtil {
/**
* 生成二维码
*
* @param content 二维码的内容
* @return BitMatrix对象
*/
public static BitMatrix createCode(String content) {
//二维码的宽高
int width = 200;
int height = 200;
//其他参数,如字符集编码
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
//容错级别为H
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//白边的宽度,可取0~4
hints.put(EncodeHintType.MARGIN, 0);
BitMatrix bitMatrix = null;
try {
//生成矩阵,因为我的业务场景传来的是编码之后的URL,所以先解码
bitMatrix = new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, width, height, hints);
//bitMatrix = deleteWhite(bitMatrix);
} catch (WriterException e) {
e.printStackTrace();
}
return bitMatrix;
}
/**
* 删除生成的二维码周围的白边,根据审美决定是否删除
*
* @param matrix BitMatrix对象
* @return BitMatrix对象
*/
private static BitMatrix deleteWhite(BitMatrix matrix) {
int[] rec = matrix.getEnclosingRectangle();
int resWidth = rec[2] + 1;
int resHeight = rec[3] + 1;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
resMatrix.clear();
for (int i = 0; i < resWidth; i++) {
for (int j = 0; j < resHeight; j++) {
if (matrix.get(i + rec[0], j + rec[1]))
resMatrix.set(i, j);
}
}
return resMatrix;
}
}
package com.qhzx.common.utils;
import com.qhzx.common.dto.CodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class Result<T> implements Serializable {
/**
*
*/
private static final long serialVersionUID = -4696008537295855861L;
// private T data;
// private Integer code;
// private String message;
/**
* 响应详情数据
*/
private T data;
private Integer code;
/**
* 响应详情说明
*/
private String message;
/**
* 200响应结果
*/
public static final String SUCCESS = "success";
/**
* 失败响应结果
*/
public static final String FAIL = "fail";
/**
* 响应错误代码
*/
public static final int ERROR_CORD = 500;
/**
* 默认构造器
*/
public Result() {
this.setCode(200);
}
/**
* 状态码构造器
*/
public Result(int responseCode) {
setCode(responseCode);
this.setMessage("null");
this.setData(null);
}
/**
* 状态码 + 消息详情构造器
*/
public Result(Integer responseCode, String message) {
this.setCode(responseCode);
this.setMessage(message);
this.setData(null);
}
public Result(Integer code) {
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public static <T> Result<T> succeed(T data) {
return succeedWith(data, CodeEnum.SUCCESS.getCode(), "success");
}
public static <T> Result<T> succeed(T model, String msg) {
return succeedWith(model, CodeEnum.SUCCESS.getCode(), msg);
}
public static <T> Result<T> succeedWith(T data, Integer code, String message) {
return new Result<T>(data, code, message);
}
public static <T> Result<T> failed(String msg) {
return failedWith(null, CodeEnum.ERROR.getCode(), msg);
}
public static <T> Result<T> failed(Integer code, String msg) {
return failedWith(null, code, msg);
}
public static <T> Result<T> failed(T model, String msg) {
return failedWith(model, CodeEnum.ERROR.getCode(), msg);
}
public static <T> Result<T> failedWith(T datas, Integer code, String msg) {
return new Result<T>(datas, code, msg);
}
public static <T> Result<T> failed(Integer code, T result) {
Result<T> jsonResult = new Result<T>(code);
jsonResult.setData(result);
jsonResult.setMessage(result + "");
return jsonResult;
}
public static <T> Result<T> initSuccessResult(T result) {
Result<T> jsonResult = new Result<T>(200);
if (null != result) {
jsonResult.setData(result);
jsonResult.setMessage(Result.SUCCESS);
}
return jsonResult;
}
/**
* 状态码 + 消息详情构造器
*/
public Result(int responseCode, String message) {
this.setCode(responseCode);
this.setMessage(message);
this.setData(null);
}
}
GoogleAuthController
package com.xiaoq.galaxy.controller;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.xiaoq.galaxy.util.GoogleGenerator;
import com.xiaoq.galaxy.util.OperLog;
import com.xiaoq.galaxy.util.QRCodeUtil;
import com.xiaoq.galaxy.util.Result;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
@CrossOrigin
@RestController
@RequestMapping(value = "/googleAuth")
public class GoogleAuthController {
//根据user和secret生成二维码的密钥
@PostMapping(value = "/getQRBarcodeURL")
@OperLog(operModul = "后台系统-getQRBarcodeURL",operDesc = "getQRBarcodeURL")
public Result<String> getQRBarcodeURL(String user, String host, String secret) {
return Result.succeed(GoogleGenerator.getQRBarcodeURL(user, host, secret));
}
//查看google 二维码信息
@PostMapping(value = "/getQRBarcode")
@OperLog(operModul = "后台系统-getQRBarcode",operDesc = "getQRBarcode")
public Result<String> getQRBarcode(String user, String secret) {
return Result.succeed(GoogleGenerator.getQRBarcode(user, secret));
}
/**
* 生成二维码
*/
@GetMapping(value = "/generateQRCode")
@OperLog(operModul = "后台系统-生成二维码",operDesc = "生成二维码")
public void GenerateQRCode(String content, HttpServletResponse response) throws IOException {
// 设置响应流信息
response.setContentType("image/jpg");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
OutputStream stream = response.getOutputStream();
//获取一个二维码图片
BitMatrix bitMatrix = QRCodeUtil.createCode(content);
//以流的形式输出到前端
MatrixToImageWriter.writeToStream(bitMatrix, "jpg", stream);
}
//新增用户的时候生成密钥并且保存
@GetMapping(value = "/geSecretKey")
@OperLog(operModul = "后台系统-geSecretKey",operDesc = "geSecretKey")
public Result<String> geSecretKey() {
return Result.succeed(GoogleGenerator.generateSecretKey());
}
//验证code是否合法
@PostMapping(value = "/checkValidCode")
@OperLog(operModul = "后台系统-checkValidCode",operDesc = "checkValidCode")
public Result<Boolean> checkGoogleValidCode(String secret, String code) {
return Result.succeed(new GoogleGenerator().check_code(secret, code, System.currentTimeMillis()));
}
}