谷歌验证器

您所在的位置:网站首页 谷歌浏览器扫描二维码在哪扫 谷歌验证器

谷歌验证器

2023-12-16 14:06| 来源: 网络整理| 查看: 265

Springboot 整合谷歌身份验证器实现两步认证

使用说明 提供了两种绑定方式:

1.密钥绑定 http://localhost:8080/google-auth/getSecretKey

获取密钥后,打开谷歌身份验证器输入账户名,密钥完成绑定

2.扫码绑定 http://localhost:8080/google-auth/getQrcode?name=xxxx

获取二维码后,打开谷歌身份验证器扫描二维码完成绑定

3.获取验证码 http://localhost:8080/google-auth/getCode?secretKey=xxxxxx 4.验证 http://localhost:8080/google-auth/checkCode?secretKey=xxxxxx&code=xxxxxx

返回 success 则表示成功,返回 error 则表示失败

谷歌身份验证器APP下载

http://d7.xiaotongqq.com/googe.apk 自己写的APP使用 使用 HBuildeX 导入项目,更改page/index/index.vue 页面中的 baseUrl

// 请求地址 baseUrl: 'http://ip:port/google-auth/',

选择运行到浏览器,或者运行到手机或模拟器(手机需要用数据线连接电脑),或者云打包成apk安装到手机

效果预览 在这里插入图片描述

pom文件

com.google.zxing core 3.4.0 com.google.zxing javase 3.4.0 commons-codec commons-codec 1.15

CorsConfigurer

package com.asurplus.common.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 跨域核心配置类 * * @Author Asurplus */ @Configuration public class CorsConfigurer { /** * 跨域配置 */ @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); // 设置允许凭据 config.setAllowCredentials(true); // 设置访问源地址 config.addAllowedOrigin("*"); // 设置访问源请求头 config.addAllowedHeader("*"); // 设置访问源请求方法 config.addAllowedMethod("*"); // 对接口配置跨域设置 source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }

GoogleAuthenticator

package com.asurplus.common.google; import org.apache.commons.codec.binary.Base32; import org.apache.commons.codec.binary.Hex; import org.springframework.util.StringUtils; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** * 谷歌身份验证器工具类 */ public class GoogleAuthenticator { /** * 时间前后偏移量 * 用于防止客户端时间不精确导致生成的TOTP与服务器端的TOTP一直不一致 * 如果为0,当前时间为 10:10:15 * 则表明在 10:10:00-10:10:30 之间生成的TOTP 能校验通过 * 如果为1,则表明在 * 10:09:30-10:10:00 * 10:10:00-10:10:30 * 10:10:30-10:11:00 之间生成的TOTP 能校验通过 * 以此类推 */ private static int WINDOW_SIZE = 0; /** * 加密方式,HmacSHA1、HmacSHA256、HmacSHA512 */ private static final String CRYPTO = "HmacSHA1"; /** * 生成密钥,每个用户独享一份密钥 * * @return */ public static String getSecretKey() { SecureRandom random = new SecureRandom(); byte[] bytes = new byte[20]; random.nextBytes(bytes); Base32 base32 = new Base32(); String secretKey = base32.encodeToString(bytes); // make the secret key more human-readable by lower-casing and // inserting spaces between each group of 4 characters return secretKey.toUpperCase(); } /** * 生成二维码内容 * * @param secretKey 密钥 * @param account 账户名 * @param issuer 网站地址(可不写) * @return */ public static String getQrCodeText(String secretKey, String account, String issuer) { String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase(); try { return "otpauth://totp/" + URLEncoder.encode((!StringUtils.isEmpty(issuer) ? (issuer + ":") : "") + account, "UTF-8").replace("+", "%20") + "?secret=" + URLEncoder.encode(normalizedBase32Key, "UTF-8").replace("+", "%20") + (!StringUtils.isEmpty(issuer) ? ("&issuer=" + URLEncoder.encode(issuer, "UTF-8").replace("+", "%20")) : ""); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } /** * 获取验证码 * * @param secretKey * @return */ public static String getCode(String secretKey) { String normalizedBase32Key = secretKey.replace(" ", "").toUpperCase(); Base32 base32 = new Base32(); byte[] bytes = base32.decode(normalizedBase32Key); String hexKey = Hex.encodeHexString(bytes); long time = (System.currentTimeMillis() / 1000) / 30; String hexTime = Long.toHexString(time); return TOTP.generateTOTP(hexKey, hexTime, "6", CRYPTO); } /** * 检验 code 是否正确 * * @param secret 密钥 * @param code code * @param time 时间戳 * @return */ public static boolean checkCode(String secret, long code, long time) { Base32 codec = new Base32(); byte[] decodedKey = codec.decode(secret); // convert unix msec time into a 30 second "window" // this is per the TOTP spec (see the RFC for details) long t = (time / 1000L) / 30L; // Window is used to check codes generated in the near past. // You can use this value to tune how far you're willing to go. long hash; for (int i = -WINDOW_SIZE; i hash = verifyCode(decodedKey, t + i); } catch (Exception e) { // Yes, this is bad form - but // the exceptions thrown would be rare and a static // configuration problem // e.printStackTrace(); throw new RuntimeException(e.getMessage()); } if (hash == code) { return true; } } return false; } /** * 根据时间偏移量计算 * * @param key * @param t * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException */ private static long verifyCode(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, CRYPTO); Mac mac = Mac.getInstance(CRYPTO); mac.init(signKey); byte[] hash = mac.doFinal(data); int offset = hash[20 - 1] & 0xF; // We're using a long because Java hasn't got unsigned int. long truncatedHash = 0; for (int i = 0; i for (int i = 0; i // 0 1 2 3 4 5 6 7 8 private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; /** * This method uses the JCE to provide the crypto algorithm. HMAC computes a * Hashed Message Authentication Code with the crypto hash algorithm as a * parameter. * * @param crypto : the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512) * @param keyBytes : the bytes to use for the HMAC key * @param text : the message or text to be authenticated */ private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) { try { Mac hmac; hmac = Mac.getInstance(crypto); SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW"); hmac.init(macKey); return hmac.doFinal(text); } catch (GeneralSecurityException gse) { throw new UndeclaredThrowableException(gse); } } /** * This method converts a HEX string to Byte[] * * @param hex : the HEX string * @return: a byte array */ private static byte[] hexStr2Bytes(String hex) { // Adding one byte to get the right conversion // Values starting with "0" can be converted byte[] bArray = new BigInteger("10" + hex, 16).toByteArray(); // Copy all the REAL bytes, not the "first" byte[] ret = new byte[bArray.length - 1]; System.arraycopy(bArray, 1, ret, 0, ret.length); return ret; } /** * This method generates a TOTP value for the given set of parameters. * * @param key : the shared secret, HEX encoded * @param time : a value that reflects a time * @param returnDigits : number of digits to return * @param crypto : the crypto function to use * @return: a numeric String in base 10 that includes */ public static String generateTOTP(String key, String time, String returnDigits, String crypto) { int codeDigits = Integer.decode(returnDigits); String result = null; // Using the counter // First 8 bytes are for the movingFactor // Compliant with base RFC 4226 (HOTP) while (time.length() result = "0" + result; } return result; } } package com.qhzx.td.controller.person; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.qhzx.td.annotation.OperLog; import com.qhzx.td.untils.*; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import org.iherus.codegen.qrcode.SimpleQrcodeGenerator; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import static com.qhzx.td.untils.RandomUtils.getNum; @Api(tags = "谷歌验证") @RestController @RequestMapping("/asurplus") @CrossOrigin @Slf4j public class AsurplusController { /** * 生成 Google 密钥,两种方式任选一种 */ @GetMapping("getSecretKey") public String getSecretKey() { return GoogleAuthenticator.getSecretKey(); } /** * 获取code */ @GetMapping("getCode") public String getCode(@RequestParam("secretKey") String secretKey) { return GoogleAuthenticator.getCode(secretKey); } /** * 验证 code 是否正确 */ @GetMapping("checkCode") public Boolean checkCode(@RequestParam("secretKey") String secretKey, @RequestParam("code") String code) { return GoogleAuthenticator.checkCode(secretKey, Long.parseLong(code), System.currentTimeMillis()); } /** * 生成二维码转base64Pic */ @GetMapping("getQrcodes") public String getQrcodes(@RequestParam String secretKey) throws Exception { Long num = getNum(5);//随机生成五位的邀请码 String base64Pic = QrCodeUtils.creatRrCode(GoogleGenerator.getQRBarcode(num.toString(), secretKey), 200,200); return base64Pic; } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3