行业知识
【java】java引入google验证
2026-10-20 01:00:14
来源:互联网转载
1. maven引入
com.google.zxing javase 3.3.0
2. google 工具类
import org.apache.commons.codec.binary.Base32import org.apache.commons.codec.binary.Heximport org.springframework.stereotype.Componentimport java.io.UnsupportedEncodingExceptionimport java.net.URLEncoderimport java.security.InvalidKeyExceptionimport java.security.NoSuchAlgorithmExceptionimport java.security.SecureRandomimport javax.crypto.Macimport javax.crypto.spec.SecretKeySpecimport kotlin.experimental.and@Componentclass GoogleAuthenticatorUtil { /** * 时间前后偏移量 * 用于防止客户端时间不精确导致生成的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 var WINDOW_SIZE = 0 /** * 加密方式,HmacSHA1、HmacSHA256、HmacSHA512 */ private var CRYPTO = "HmacSHA1" /** * 生成密钥,每个用户独享一份密钥 * * @return */ fun getSecretKey(): String { val random = SecureRandom() val bytes = ByteArray(20) random.nextBytes(bytes) val base32 = Base32() val 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 */ fun getQrCodeText(secretKey: String, account: String, issuer: String): String { val normalizedBase32Key = secretKey.replace(" ", "").toUpperCase() return try { "otpauth://totp/" + URLEncoder.encode((if (issuer.isNotEmpty()) "$issuer:" else "") + account, "UTF-8") .replace("+", "%20") + "?secret=" + URLEncoder.encode(normalizedBase32Key, "UTF-8").replace("+", "%20") + if (issuer.isNotEmpty()) "&issuer=" + URLEncoder.encode(issuer, "UTF-8").replace("+", "%20") else "" } catch (e: UnsupportedEncodingException) { throw IllegalStateException(e) } } /** * 获取验证码 * * @param secretKey * @return */ fun getCode(secretKey: String): String { val normalizedBase32Key = secretKey.replace(" ", "").toUpperCase() val base32 = Base32() val bytes = base32.decode(normalizedBase32Key) val hexKey = Hex.encodeHexString(bytes) val time = System.currentTimeMillis() / 1000 / 30 val hexTime = java.lang.Long.toHexString(time) return generateTOTP(hexKey, hexTime, "6", CRYPTO) } /** * 检验 code 是否正确 * * @param secret 密钥 * @param code code * @param time 时间戳 * @return */ fun checkCode(secret: String, code: Long, time: Long): Boolean { val codec = Base32() val decodedKey = codec.decode(secret) // convert unix msec time into a 30 second "window" // this is per the TOTP spec (see the RFC for details) var 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. var hash: Long for (i in -WINDOW_SIZE..WINDOW_SIZE) { try { hash = verifyCode(decodedKey, t + i) } catch (e: Exception) { // Yes, this is bad form - but // the exceptions thrown would be rare and a static // configuration problem // e.printStackTrace(); throw RuntimeException(e.message) } if (hash == code) { return true } } return false } /** * 根据时间偏移量计算 * * @param key * @param t * @return * @throws NoSuchAlgorithmException * @throws InvalidKeyException */ private fun verifyCode(key: ByteArray, t: Long): Long { val data = ByteArray(8) var value = t for (i in 7 downTo 0) { data[i] = value.toByte() value = value shr 8 } val signKey = SecretKeySpec(key, CRYPTO) val mac = Mac.getInstance(CRYPTO) mac.init(signKey) val hash = mac.doFinal(data) var offset = hash[20 - 1] and 0xF.toByte() // We're using a long because Java hasn't got unsigned int. var truncatedHash = 0L for (i in 0..3) { truncatedHash = truncatedHash shl 8 // We are dealing with signed bytes: // we just keep the first byte. truncatedHash = truncatedHash or (hash[offset + i].toInt() and 0xFF).toLong() } truncatedHash = truncatedHash and 0x7FFFFFFF truncatedHash %= 1000000 return truncatedHash } private fun generateTOTP( key: String, time: String?, returnDigits: String, crypto: String ): String { var time = time var code = "" val tm = time!!.toLong(16) val hash = hmacSha(crypto, hexStr2Bytes(key), longToByteArray(tm)) var offset = hash[hash.size - 1] and 0xf val binary = ((hash[offset.toInt()].toInt() and 0x7f) shl 24 or (hash[offset + 1].toInt() and 0xff) shl 16 or (hash[offset + 2].toInt() and 0xff) shl 8 or (hash[offset + 3].toInt() and 0xff)) .toLong() val otp = binary % DIGITS_POWER[returnDigits.toInt()] code = otp.toString() while (code.length { c - '0' } in 'a'..'f' -> { c - 'a' + 10 } in 'A'..'F' -> { c - 'A' + 10 } else -> { throw IllegalArgumentException("Invalid hex character: $c") } } // Shift high nibble four bits to the left val shiftedNibble = nibble shl 4 // Get next char from string val nextChar = hex[i + 1] // Gather low nibble val nextNibble: Int = when (nextChar) { in '0'..'9' -> { nextChar - '0' } in 'a'..'f' -> { nextChar - 'a' + 10 } in 'A'..'F' -> { nextChar - 'A' + 10 } else -> { throw IllegalArgumentException("Invalid hex character: $nextChar") } } // Combine high nibble with low nibble val b = shiftedNibble or nextNibble // Store byte in array bArray[j++] = b.toByte() } // Remove trailing zeros return if (bArray.size == j) { bArray } else { bArray.copyOf(j) } } private fun longToByteArray(value: Long): ByteArray { val buffer = ByteArray(8) for (i in 7 downTo 0) { buffer[i] = (value shr 8 * (7 - i)).toByte() } return buffer } private val DIGITS_POWER = intArrayOf( 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 )} 3. kotlin业务代码实现
/** 3. 绑定谷歌验证 */@RestfulPack@SaCheckLogin@PostMapping("bingGoogleAuth")fun bingGoogleAuth(@RequestBody bindGoogleAuthCodeDto: BindGoogleAuthCodeDto): Boolean { if (StringUtils.isNotBlank(user.googleAuth)) { throw ProgramException(i18nUtils.getMessage("UNALLOWED_OPERATIONS")!!) } return if (googleAuthenticator.checkCode( bindGoogleAuthCodeDto.secretKey, bindGoogleAuthCodeDto.code, System.currentTimeMillis() ) ) { val encryptedMessage = TripleDesUtils.encrypt(bindGoogleAuthCodeDto.secretKey, user.salt) user.googleAuth = encryptedMessage user.bindGoogleAuth=true true } else { throw ProgramException(i18nUtils.getMessage("INVALID_GOOGLE_VERIFICATION_CODE")!!) }}/** * 验证 code 是否正确 */@RestfulPack@GetMapping("checkCode")fun checkCode(@RequestParam("code") code: Long): Boolean { val user = UserUtil.get() if (StringUtils.isBlank(user.googleAuth)){ throw ProgramException(i18nUtils.getMessage("UNALLOWED_OPERATIONS")!!) } return googleAuthenticator.checkCode(TripleDesUtils.decrypt(user.googleAuth!!,user.salt), code, System.currentTimeMillis());}/** * 验证 code 是否正确 */@RestfulPack@PostMapping("removeGoogleAuth")fun removeGoogleAuth(@RequestBody bindGoogleAuthCodeDto: BindGoogleAuthCodeDto): Boolean { val user = UserUtil.get() if (StringUtils.isBlank(user.googleAuth)){ throw ProgramException(i18nUtils.getMessage("UNALLOWED_OPERATIONS")!!) } return if (googleAuthenticator.checkCode(TripleDesUtils.decrypt(user.googleAuth!!,user.salt), bindGoogleAuthCodeDto.code, System.currentTimeMillis())==true){ user.googleAuth = null user.bindGoogleAuth=false true }else{ throw ProgramException(i18nUtils.getMessage("INVALID_GOOGLE_VERIFICATION_CODE")!!) }} ()()
上一篇:更换主机的电源多少起那
下一篇:服务器上如何清理缓存?