Java实现谷歌验证器

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()));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值