From: kaixiang.xia Date: Fri, 4 Dec 2020 08:48:14 +0000 (+0800) Subject: 两码转换支持配置 X-Git-Tag: 1.0.29^2 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=27a604ca5e94ce40ce24736eb35d8acf10c42540;p=epayment%2Ffood_payapi.git 两码转换支持配置 --- diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt index 5560a5d3..a55d1e9d 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt @@ -1,5 +1,6 @@ package com.supwisdom.dlpay.api.service.impl +import com.google.gson.Gson import com.supwisdom.dlpay.api.bean.ApiResponse import com.supwisdom.dlpay.api.bean.CitizenQrcodeKey import com.supwisdom.dlpay.api.bean.DoorQrcodeResponse @@ -13,12 +14,14 @@ import com.supwisdom.dlpay.framework.util.DateUtil import com.supwisdom.dlpay.framework.util.StringUtil import com.supwisdom.dlpay.framework.util.TradeDict import com.supwisdom.dlpay.mobile.service.MobileApiService -import com.supwisdom.dlpay.util.ConstantUtil +import com.supwisdom.dlpay.util.* import mu.KotlinLogging import org.apache.commons.lang.math.NumberUtils import org.springframework.beans.factory.annotation.Autowired import org.springframework.data.redis.core.RedisTemplate import org.springframework.stereotype.Service +import com.supwisdom.dlpay.framework.util.MD5 +import java.time.Duration @Service class QRCodeServiceImpl:QRCodeService{ @@ -39,16 +42,19 @@ class QRCodeServiceImpl:QRCodeService{ var step = "5" var offset = "3" var debug = "false" + var offlineQrcode = "false" val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey")?.trim() ?: "" val iv = systemUtilService.getBusinessValue("aes.cfb.iv")?.trim() ?: "" val prefix = systemUtilService.getBusinessValue("dlsmk.qrcode.prefix")?.trim() ?: "" - val totpStep = systemUtilService.getBusinessValue("dlsmk.qrcode.totp.step") + val totpStep = systemUtilService.getBusinessValue("dlsmk.qrcode.totp.step") //fixme: jar包写死了5s val totpOffset = systemUtilService.getBusinessValue("dlsmk.qrcode.totp.offset") val debugEnable = systemUtilService.getBusinessValue("dlsmk.qrcode.log.debug") + val newQrcode = systemUtilService.getBusinessValue("dlsmk.qrcode.offline.enable") if (NumberUtils.isDigits(totpStep)) step = totpStep.trim() if (NumberUtils.isDigits(totpOffset)) offset = totpOffset.trim() if ("true".equals(debugEnable, true)) debug = "true" - return mapOf("rootkey" to rootkey, "iv" to iv, "prefix" to prefix, "step" to step, "offset" to offset, "debug" to debug) + if ("true".equals(newQrcode, true)) offlineQrcode = "true" + return mapOf("rootkey" to rootkey, "iv" to iv, "prefix" to prefix, "step" to step, "offset" to offset, "debug" to debug, "offline" to offlineQrcode) } override fun encodeCode(uid: String): ApiResponse { @@ -58,6 +64,7 @@ class QRCodeServiceImpl:QRCodeService{ val iv = config["iv"] val prefix = config["prefix"] ?: "" val debug = "true" == config["debug"] + val offlineQRCode = "true" == config["offline"] if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) { resp.retcode = 1 @@ -65,12 +72,12 @@ class QRCodeServiceImpl:QRCodeService{ return resp } val muser = mobileApiService.findUserById(uid) - if(muser==null||TradeDict.STATUS_NORMAL!=muser.status){ + if (muser == null || TradeDict.STATUS_NORMAL != muser.status) { resp.retcode = 1 resp.retmsg = "用户不存在或状态异常" return resp } - if(muser.userid.isNullOrEmpty()){ + if (muser.userid.isNullOrEmpty()) { resp.retcode = 1 resp.retmsg = "用户未绑定身份" return resp @@ -84,7 +91,18 @@ class QRCodeServiceImpl:QRCodeService{ resp.retcode = 1 resp.retmsg = "银行卡未签约" return resp + } else if (TradeDict.STATUS_NORMAL != bankCard.transStatus) { + resp.retcode = 1 + resp.retmsg = when (bankCard.transStatus) { + TradeDict.STATUS_UNUSE -> "银行卡未启用!" + TradeDict.STATUS_LOST -> "银行卡已挂失!" + TradeDict.STATUS_LOCKED -> "银行卡已锁定!" + TradeDict.STATUS_FROZEN -> "银行卡已冻结!" + else -> "银行卡状态异常!" + } + return resp } + val cityCard = cardService.findCardByUseridAndCardphyid(bankCard.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankCard.cardphyid) if (null == cityCard) { resp.retcode = 1 @@ -106,41 +124,47 @@ class QRCodeServiceImpl:QRCodeService{ return resp } - try { - val handle = QrCode.builder() - .rootKey(rootkey) - .iv(iv) - .uid(uid) - .card(cityCard.cardno, cityCard.busCardType ?: "80") - .prefix(prefix) - .debug(debug) - .create() - val qrcode = handle.qrcode() + if (offlineQRCode) { + //脱机码,新版本统一码 + try { + val handle = QrCode.builder() + .rootKey(rootkey) + .iv(iv) + .uid(uid) + .card(cityCard.cardno, cityCard.busCardType ?: "80") + .prefix(prefix) + .debug(debug) + .create() + val qrcode = handle.qrcode() + resp.retcode = 0 + resp.retmsg = qrcode + return resp + } catch (ex: Exception) { + ex.printStackTrace() + resp.retcode = 1 + resp.retmsg = "二维码生成失败!${ex.message}" + return resp + } + + } else { + //老版本H5码,存redis + val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey) + val rowdata = QrcodeRawData() + rowdata.userid = muser.userid + rowdata.setTotp(totp) + val orgData = Gson().toJson(rowdata) + val publicKey = RSAKeysGenerate.getPublicKey(muser.rsapublic) + val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData)) + val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding") + + val key = MD5.encodeByMD5ToURLSafeBase64(qrcode) + redisTemplate.opsForValue().set(key, qrcode, Duration.ofSeconds(20)) + resp.retcode = 0 - resp.retmsg = qrcode - return resp - } catch (ex: Exception) { - ex.printStackTrace() - resp.retcode = 1 - resp.retmsg = "二维码生成失败!${ex.message}" + resp.retmsg = key return resp } -// val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey) -// val rowdata = QrcodeRawData() -// rowdata.userid = muser.userid -// rowdata.setTotp(totp) -// val orgData = Gson().toJson(rowdata) -// val publicKey = RSAKeysGenerate.getPublicKey(muser.rsapublic) -// val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData)) -// val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding") -// -// val key = MD5.encodeByMD5ToURLSafeBase64(qrcode) -// redisTemplate.opsForValue().set(key,qrcode, Duration.ofSeconds(20)) - -// resp.retcode = 0 -// resp.retmsg = key -// return resp } override fun encodeCode(param: QrcodeParam): ApiResponse { @@ -151,6 +175,7 @@ class QRCodeServiceImpl:QRCodeService{ val iv = config["iv"] val prefix = config["prefix"] ?: "" val debug = "true" == config["debug"] + val offlineQRCode = "true" == config["offline"] if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) { resp.retcode = 1 @@ -177,7 +202,18 @@ class QRCodeServiceImpl:QRCodeService{ resp.retcode = 1 resp.retmsg = "银行卡未签约" return resp + } else if (TradeDict.STATUS_NORMAL != bankCard.transStatus) { + resp.retcode = 1 + resp.retmsg = when (bankCard.transStatus) { + TradeDict.STATUS_UNUSE -> "银行卡未启用!" + TradeDict.STATUS_LOST -> "银行卡已挂失!" + TradeDict.STATUS_LOCKED -> "银行卡已锁定!" + TradeDict.STATUS_FROZEN -> "银行卡已冻结!" + else -> "银行卡状态异常!" + } + return resp } + val cityCard = cardService.findCardByUseridAndCardphyid(bankCard.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankCard.cardphyid) if (null == cityCard) { resp.retcode = 1 @@ -199,40 +235,47 @@ class QRCodeServiceImpl:QRCodeService{ return resp } - try { - val handle = QrCode.builder() - .rootKey(rootkey) - .iv(iv) - .uid(uid) - .card(cityCard.cardno, cityCard.busCardType ?: "80") - .prefix(prefix) - .debug(debug) - .create() - val qrcode = handle.qrcode() + if (offlineQRCode) { + //脱机码,新版本统一码 + try { + val handle = QrCode.builder() + .rootKey(rootkey) + .iv(iv) + .uid(uid) + .card(cityCard.cardno, cityCard.busCardType ?: "80") + .prefix(prefix) + .debug(debug) + .create() + val qrcode = handle.qrcode() + resp.retcode = 0 + resp.retmsg = qrcode + return resp + } catch (ex: Exception) { + ex.printStackTrace() + resp.retcode = 1 + resp.retmsg = "二维码生成失败!${ex.message}" + return resp + } + + } else { + //老版本H5码,存redis + val totp = QrCodeTotpUtil.generateTOTP(userSec.secertkey) + val rowdata = QrcodeRawData() + rowdata.userid = userSec.userid + rowdata.setTotp(totp) + val orgData = Gson().toJson(rowdata) + val publicKey = RSAKeysGenerate.getPublicKey(userSec.rsapublic) + val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData)) + val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding") + + val key = MD5.encodeByMD5ToURLSafeBase64(qrcode) + redisTemplate.opsForValue().set(key, qrcode, Duration.ofSeconds(20)) + resp.retcode = 0 - resp.retmsg = qrcode - return resp - } catch (ex: Exception) { - ex.printStackTrace() - resp.retcode = 1 - resp.retmsg = "二维码生成失败!${ex.message}" + resp.retmsg = key return resp } -// val totp = QrCodeTotpUtil.generateTOTP(param.secertkey) -// val rowdata = QrcodeRawData() -// rowdata.userid = param.userid -// rowdata.setTotp(totp) -// val orgData = Gson().toJson(rowdata) -// val publicKey = RSAKeysGenerate.getPublicKey(param.rsapublic) -// val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData)) -// val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding") -// -// val key = MD5.encodeByMD5ToURLSafeBase64(qrcode) -// redisTemplate.opsForValue().set(key,qrcode, Duration.ofSeconds(20)) -// resp.retcode = 0 -// resp.retmsg = key -// return resp } override fun decodeCode(qrcode: String): DoorQrcodeResponse { @@ -242,11 +285,20 @@ class QRCodeServiceImpl:QRCodeService{ val iv = config["iv"] val prefix = config["prefix"] ?: "" val debug = "true" == config["debug"] - val totpoffset = config["offset"] + val offlineQRCode = "true" == config["offline"] var offset = 3 - if (NumberUtils.isDigits(totpoffset)) { - offset = totpoffset!!.toInt() + if (offlineQRCode) { + val totpoffset = config["offset"] + if (NumberUtils.isDigits(totpoffset)) { + offset = totpoffset!!.toInt() + } + } else { + val totpoffset = systemUtilService.getBusinessValue("aes.cfb.totp.offset") + if (NumberUtils.isDigits(totpoffset)) { + offset = Integer.valueOf(totpoffset).toInt() + } } + if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) { resp.retcode = 1 resp.retmsg = "秘钥未配置" @@ -255,56 +307,144 @@ class QRCodeServiceImpl:QRCodeService{ println("vtoken=[" + qrcode + "],length=[" + qrcode.length + "]") //解密 - var data = HashMap(0) - try { - val handle = QrCode.builder().rootKey(rootkey).iv(iv).prefix(prefix).debug(debug).create() - //fixme:解码已校验totp - data = handle.decodeWithTotpCheck(qrcode, offset) as HashMap - } catch (ex: Exception) { - ex.printStackTrace() - resp.retcode = 1 - resp.retmsg = "二维码解析报错,${ex.message}" - return resp - } - val uid = data[QrCode.FIELD_UID] - val cardno = data[QrCode.FIELD_CARDNO] - val cardtype = data[QrCode.FIELD_CARDTYPE] //公交卡类型 - if (uid.isNullOrEmpty() || cardno.isNullOrEmpty() || cardtype.isNullOrEmpty()) { - resp.retcode = 1 - resp.retmsg = "二维码解析后数据异常!" - return resp - } - val userSec = mobileApiService.findUserSecretByUid(uid) - val cityCard = cardService.findCardByCardnoAndCardtype(cardno, ConstantUtil.CARDTYPE_CITIZENCARD) - if(null==userSec || null == cityCard || cityCard.userid != userSec.userid){ - resp.retcode = 1 - resp.retmsg = "二维码用户信息错误,请刷新二维码后重试" - return resp - } - val person = userService.findPersonByUserid(cityCard.userid) - val bankcard = mobileApiService.findCardByUseridAndCardphyid(cityCard.userid, ConstantUtil.CARDTYPE_BANKCARD, cityCard.cardphyid) - if (null == person || null == bankcard) { - resp.retcode = 8 - resp.retmsg = "用户市民卡信息异常" + if(offlineQRCode){ + //脱机码,新版本统一码 + var data = HashMap(0) + try { + val handle = QrCode.builder().rootKey(rootkey).iv(iv).prefix(prefix).debug(debug).create() + //fixme:解码已校验totp + data = handle.decodeWithTotpCheck(qrcode, offset) as HashMap + } catch (ex: Exception) { + ex.printStackTrace() + resp.retcode = 1 + resp.retmsg = "二维码解析报错,${ex.message}" + return resp + } + val uid = data[QrCode.FIELD_UID] + val cardno = data[QrCode.FIELD_CARDNO] + val cardtype = data[QrCode.FIELD_CARDTYPE] //公交卡类型 + if (uid.isNullOrEmpty() || cardno.isNullOrEmpty() || cardtype.isNullOrEmpty()) { + resp.retcode = 1 + resp.retmsg = "二维码解析后数据异常!" + return resp + } + val userSec = mobileApiService.findUserSecretByUid(uid) + val cityCard = cardService.findCardByCardnoAndCardtype(cardno, ConstantUtil.CARDTYPE_CITIZENCARD) + if(null==userSec || null == cityCard || cityCard.userid != userSec.userid){ + resp.retcode = 1 + resp.retmsg = "二维码用户信息错误,请刷新二维码后重试" + return resp + } + val person = userService.findPersonByUserid(cityCard.userid) + val bankcard = mobileApiService.findCardByUseridAndCardphyid(cityCard.userid, ConstantUtil.CARDTYPE_BANKCARD, cityCard.cardphyid) + if (null == person || null == bankcard) { + resp.retcode = 8 + resp.retmsg = "用户市民卡信息异常" + return resp + } + + resp.retcode = 0 + resp.retmsg = "OK" + resp.userid = person.userid + resp.username = person.name + resp.sex = person.sex + resp.idtype = person.idtype + resp.idno = person.idno + resp.phone = person.mobile + + resp.citycardno = cityCard.cardno + resp.cardphyid = cityCard.cardphyid + resp.expiredate = cityCard.expiredate + resp.cardstatus = cityCard.status + resp.transstatus = cityCard.transStatus + resp.bankcardno = bankcard.cardno return resp - } - resp.retcode = 0 - resp.retmsg = "OK" - resp.userid = person.userid - resp.username = person.name - resp.sex = person.sex - resp.idtype = person.idtype - resp.idno = person.idno - resp.phone = person.mobile + }else{ + //老版本H5码,存redis + try { + val token = redisTemplate.opsForValue().get(qrcode) //fixme: 密文在redis中 + if (token.isNullOrEmpty()) { + resp.retcode = 1 + resp.retmsg = "二维码不存在或已失效" + return resp + } + + val encdataBack = AesUtil.decryptCFB(token, rootkey, iv, "AES/CFB/NoPadding") + if (encdataBack == null) { + resp.retcode = 1 + resp.retmsg = "二维码解析失败" + return resp + } + val uid = encdataBack.substring(0, encdataBack.indexOf(":")) +// val muser = mobileApiService.findUserById(uid) + val muser = mobileApiService.findUserSecretByUid(uid) //查找userSecret + if (muser == null) { + resp.retcode = 1 + resp.retmsg = "用户不存在或状态异常" + return resp + } + val seed32 = muser.secertkey + val rawEncdata = encdataBack.substring(encdataBack.indexOf(":") + 1) + + val privateKey = RSAKeysGenerate.getPrivateKey(muser.rsaprivate) + val rawdataBack = RSAKeysGenerate.decryptbyte(privateKey, rawEncdata) + logger.info("rawdata_back=$rawdataBack") + val rawData = Gson().fromJson(rawdataBack, QrcodeRawData::class.java) + logger.info("userid=" + rawData.userid) + if (null == rawData || rawData.userid.isNullOrEmpty()) { + resp.retcode = 1 + resp.retmsg = "用户不存在" + return resp + } + + val person = userService.findOnePersonByUserid(rawData.userid) + val bankcard = mobileApiService.findCardByUserid(person.userid) + if (null == bankcard) { + resp.retcode = 9 + resp.retmsg = "二维码识别错误,手机绑定的银行卡已注销" + return resp + } + + //银行卡必定存在对应的市民卡 + val citycard = mobileApiService.findCardByUseridAndCardphyid(person.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankcard.cardphyid) + if (null == citycard) { + resp.retcode = 8 + resp.retmsg = "二维码识别异常" + return resp + } + + val verifyBarcode = QrCodeTotpUtil.verifyCode(rawData.totp, seed32, offset) + return if (verifyBarcode) { + resp.retcode = 0 + resp.retmsg = "OK" + resp.userid = person.userid + resp.username = person.name + resp.sex = person.sex + resp.idtype = person.idtype + resp.idno = person.idno + resp.phone = person.mobile + + resp.citycardno = citycard.cardno + resp.cardphyid = citycard.cardphyid + resp.expiredate = citycard.expiredate + resp.cardstatus = citycard.status + resp.transstatus = citycard.transStatus + resp.bankcardno = bankcard.cardno + resp + } else { + resp.retcode = 1 + resp.retmsg = "二维码校验失败" + resp + } + } catch (e: Exception) { + e.printStackTrace() + resp.retcode = 1 + resp.retmsg = "二维码识别异常" + return resp + } + } - resp.citycardno = cityCard.cardno - resp.cardphyid = cityCard.cardphyid - resp.expiredate = cityCard.expiredate - resp.cardstatus = cityCard.status - resp.transstatus = cityCard.transStatus - resp.bankcardno = bankcard.cardno - return resp } override fun decodeCode(qrcode: String, chkTotp: Boolean, cardno: String?, tac: String?, amount: Int?, transdate: String?, transtime: String?): DoorQrcodeResponse { @@ -314,6 +454,7 @@ class QRCodeServiceImpl:QRCodeService{ val iv = config["iv"] val prefix = config["prefix"] ?: "" val debug = "true" == config["debug"] + val offlineQRCode = "true" == config["offline"] val totpoffset = config["offset"] var offset = 3 if (NumberUtils.isDigits(totpoffset)) { @@ -324,6 +465,9 @@ class QRCodeServiceImpl:QRCodeService{ resp.retmsg = "秘钥未配置" return resp } + if (!offlineQRCode) { + return decodeCode(qrcode) // fixme: 未启用脱机码,这里直接老版解码 + } println("citizencard QRcode=[" + qrcode + "],length=[" + qrcode.length + "]") //解密