小工具知识网

当前位置: 首页 > 行业知识 > 【java】java引入google验证

行业知识

【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")!!)    }}
()()

上一篇:更换主机的电源多少起那

下一篇:服务器上如何清理缓存?