package com.cdzy.user.utils; import com.cdp.product.security.context.SecurityContext; import com.cdp.product.security.exception.DecryptFailureException; import com.cdp.product.security.exception.EncryptFailureException; import com.cdp.product.security.exception.SignFailureException; import com.cdp.product.security.strategyImpl.SM2StrategyImpl; import com.cdzy.common.model.response.JsonResult; import com.cdzy.common.secure.SecureComponent; import com.cdzy.common.secure.SecureSM2Component; import com.cdzy.user.config.RealNameVerifyConfig; import com.cdzy.user.config.WechatConfig; import com.cdzy.user.enums.RealNameAttestationStatus; import com.cdzy.user.model.dto.EbikeRealNameSignDto; import com.cdzy.user.model.dto.EbikeRealNameVerifyDto; import com.cdzy.user.model.dto.UserValidateDto; import com.cdzy.user.model.entity.EbikeUserRealInfo; import com.cdzy.user.model.vo.EbikeUserVo; import com.cdzy.user.service.EbikeUserRealInfoService; import com.cdzy.user.service.EbikeUserService; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.Security; import java.time.LocalDateTime; import java.util.*; /** * 验证工具类 * * @author yanglei * @since 2025-10-15 09:30 */ @Slf4j @Service public class VerifyUtil { /** * 微信登录API地址 */ private static final String WECHAT_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session"; /** * 数据宝实名认证API地址 */ private static final String REALNAME_VERIFY_URL = "https://api.chinadatapay.com/communication/personal/1882"; /** * OkHttpClient实例 */ private OkHttpClient client = null; @Resource private WechatConfig wechatConfig; @Resource private RealNameVerifyConfig realNameVerifyConfig; @Resource private EbikeUserService ebikeUserService; @Resource private EbikeUserRealInfoService ebikeUserRealInfoService; @Resource private ObjectMapper objectMapper; //暂时硬编码,后续放到redis或者config中【本地数据加密解密】 private SecureComponent secureComponent; private static final String PRIVATE_KEY = "f8209a2ebe6691e41e1f2b667bfe71f0b511716cc0f7c4452502fc12ec3957e4"; private static final String PUBLIC_KEY = "04f5084ee12767d932f293508e30e3b0100185042ec0f061dedaf92b793b93f79fd6179d5e47e25b7aec98e00cf90dd56df1f8191012537187e7bbfd2d1de299fc"; // 加解密上下⽂ private final SecurityContext securityContext; /** * 微信工具类构造函数。 */ public VerifyUtil() throws IOException { client = new OkHttpClient(); securityContext = new SecurityContext(); //设置加解密上下⽂类调⽤SM2加解密类 securityContext.setSecurityImpl(new SM2StrategyImpl()); //设置加解密上下⽂类调⽤SM2加解密类加签 securityContext.setSign(new SM2StrategyImpl()); } /** * 获取openId。 * * @param code 微信登录返回的code * @return openId */ public JsonNode wechatAuthority(String code) { Map params = new HashMap<>(); params.put("appid", wechatConfig.getAppId()); params.put("secret", wechatConfig.getAppSecret()); params.put("js_code", code); params.put("grant_type", "authorization_code"); return httpGet("获取openId", WECHAT_LOGIN_URL, params); } /** * 解密微信用户信息。 * * @param encryptedData 加密数据 * @param sessionKey 会话密钥 * @param iv 初始向量 * @return 解密后的用户信息 */ public String decryptData(String encryptedData, String sessionKey, String iv) { try { // 添加 BouncyCastle 安全提供器 Security.addProvider(new BouncyCastleProvider()); // Base64 解码参数 byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData); byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey); byte[] ivBytes = Base64.getDecoder().decode(iv); // 初始化 AES-CBC 解密器 SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(ivBytes); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); // 执行解密 byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); } catch (Exception e) { log.error("解密失败", e); return null; } } /** * 验证用户实名 * * @param userValidateDto 验证用户实名请求 * @return 验证结果 */ @Transactional public JsonResult verifyRealName(UserValidateDto userValidateDto) { EbikeUserVo userInfo = ebikeUserService.getUserInfoByUserId(userValidateDto.getUserId()); if (userInfo == null) { log.error("用户{}不存在", userValidateDto.getUserId()); return JsonResult.failed("用户不存在"); } // 校验是否已进行验证 Integer realNameStatus = userInfo.getUserRealNameStatus(); if (realNameStatus != null && realNameStatus.equals(RealNameAttestationStatus.CERTIFIED)) { return JsonResult.failed("用户已实名验证"); } try { // 1. 加密数据 String encryptedName = securityContext.encrypt(userValidateDto.getName(), realNameVerifyConfig.getServerPublicKey()); String encryptedIdCard = securityContext.encrypt(userValidateDto.getIdCard(), realNameVerifyConfig.getServerPublicKey()); String encryptedTimestamp = securityContext.encrypt(String.valueOf(System.currentTimeMillis()), realNameVerifyConfig.getServerPublicKey()); EbikeRealNameSignDto signData = new EbikeRealNameSignDto(encryptedName, encryptedIdCard, encryptedTimestamp); String sign = securityContext.sign(signData.toMap(), realNameVerifyConfig.getClientPrivateKey()); EbikeRealNameVerifyDto ebikeRealNameVerifyDto = new EbikeRealNameVerifyDto(); ebikeRealNameVerifyDto.setName(encryptedName); ebikeRealNameVerifyDto.setIdCard(encryptedIdCard); ebikeRealNameVerifyDto.setTimestamp(encryptedTimestamp); ebikeRealNameVerifyDto.setSign(sign); ebikeRealNameVerifyDto.setKey(realNameVerifyConfig.getApiKey()); // 2. 验证用户实名 JsonNode result = httpPost("验证用户实名", REALNAME_VERIFY_URL, ebikeRealNameVerifyDto, null); log.info("验证用户实名结果:{}", result); if (result == null) { log.error("验证用户实名失败"); return JsonResult.failed("验证用户实名失败"); } if (result.has("code") && "10000".equals(result.get("code").asText())) { // 3. 解密结果 String data = securityContext.decrypt(result.get("data").asText(), realNameVerifyConfig.getClientPrivateKey()); JsonNode jsonData = objectMapper.readTree(data); if (jsonData.has("result") && "1".equals(jsonData.get("result").asText())) { // 4. 更新用户实名信息 updateRealNameInfo(userValidateDto, true); return JsonResult.success("验证用户实名成功"); } } updateRealNameInfo(userValidateDto, false); String errorCode = result.has("code") ? result.get("code").asText() : "未知错误"; String errorMessage = result.has("message") ? result.get("message").asText() : "验证失败"; log.error("验证用户实名失败,{} {}", errorCode, errorMessage); return JsonResult.failed(errorCode + ", " + errorMessage); } catch (EncryptFailureException e) { log.error("加密失败", e); return JsonResult.failed("加密失败"); } catch (SignFailureException e) { log.error("签名失败", e); return JsonResult.failed("签名失败"); } catch (DecryptFailureException e) { log.error("结果解密失败", e); return JsonResult.failed("结果解密失败"); } catch (JsonProcessingException e) { log.error("JSON解析失败", e); return JsonResult.failed("数据解析失败"); } } /** * 更新用户实名信息 * * @param userValidateDto 用户验证信息 * @param isValid 是否通过验证 */ private void updateRealNameInfo(UserValidateDto userValidateDto, boolean isValid) { EbikeUserRealInfo ebikeUserRealinfo = ebikeUserRealInfoService.getRealInfoByUserId(userValidateDto.getUserId()); String realName = userValidateDto.getName(); String idCard = userValidateDto.getIdCard(); if (ebikeUserRealinfo == null) { ebikeUserRealinfo = new EbikeUserRealInfo(); ebikeUserRealinfo.setUserId(userValidateDto.getUserId()); ebikeUserRealinfo.setUserRealName(getSecureComponent().encrypt(realName, PUBLIC_KEY)); ebikeUserRealinfo.setUserIdCard(getSecureComponent().encrypt(idCard, PUBLIC_KEY)); ebikeUserRealinfo.setAttestationStatus(isValid ? RealNameAttestationStatus.CERTIFIED : RealNameAttestationStatus.PENDING_REVIEW); ebikeUserRealinfo.setAuditTime(LocalDateTime.now()); ebikeUserRealInfoService.save(ebikeUserRealinfo); } else { ebikeUserRealinfo.setUserRealName(getSecureComponent().encrypt(realName, PUBLIC_KEY)); ebikeUserRealinfo.setUserIdCard(getSecureComponent().encrypt(idCard, PUBLIC_KEY)); ebikeUserRealinfo.setAttestationStatus(isValid ? RealNameAttestationStatus.CERTIFIED : RealNameAttestationStatus.PENDING_REVIEW); ebikeUserRealinfo.setAuditTime(LocalDateTime.now()); ebikeUserRealInfoService.updateById(ebikeUserRealinfo); } } /** * 发送HTTP GET请求。 * * @param func_ 功能描述 * @param url 请求URL * @param params 请求参数 * @return 响应结果 */ private JsonNode httpGet(String func_, String url, Map params) { StringJoiner paramJoiner = new StringJoiner("&"); for (Map.Entry entry : params.entrySet()) { paramJoiner.add(entry.getKey() + "=" + entry.getValue()); } String requestUrl = url + "?" + paramJoiner.toString(); Request request = new Request.Builder() .url(requestUrl) .get() .build(); return executeAndParseResponse(func_, url, request); } /** * 发送HTTP POST参数请求。 * * @param func_ 功能描述 * @param url 请求URL * @param dto 请求参数 * @return 响应结果 */ private JsonNode httpPost(String func_, String url, EbikeRealNameVerifyDto dto, Map body) { StringBuilder queryString = new StringBuilder(); appendParam(queryString, "name", dto.getName()); appendParam(queryString, "idCard", dto.getIdCard()); appendParam(queryString, "timestamp", dto.getTimestamp()); appendParam(queryString, "sign", dto.getSign()); appendParam(queryString, "key", dto.getKey()); String requestUrl = url; if (!queryString.isEmpty()) { requestUrl += "?" + queryString.toString(); } Request.Builder builder = new Request.Builder().url(requestUrl); if (body != null) { MediaType typeJson = MediaType.parse("application/json; charset=utf-8"); try { String jsonBody = objectMapper.writeValueAsString(body); RequestBody requestBody = RequestBody.create(jsonBody, typeJson); builder.post(requestBody); } catch (JsonProcessingException e) { log.error("{}, 请求体序列化失败", func_, e); return null; } } else { builder.post(RequestBody.create(new byte[0])); } Request request = builder.build(); return executeAndParseResponse(func_, url, request); } private JsonNode executeAndParseResponse(String func_, String url, Request request) { try (Response response = client.newCall(request).execute()) { if (response.isSuccessful()) { if (response.body() != null) { String responseBody = response.body().string(); return objectMapper.readTree(responseBody); } log.error("{}, 请求{}无返回结果", func_, url); return null; } if (response.body() != null) { log.error("{}, 请求{}失败, {}", func_, url, response.body().string()); } else { log.error("{}, 请求{}失败", func_, url); } return null; } catch (Exception e) { log.error("{}, 请求{}失败", func_, url, e); return null; } } private void appendParam(StringBuilder sb, String key, String value) { if (value == null) { return; } if (!sb.isEmpty()) { sb.append("&"); } sb.append(key).append("=").append(value); } private SecureComponent getSecureComponent() { if (Objects.isNull(secureComponent)) { secureComponent = new SecureSM2Component(); } return secureComponent; } /** * 解密用户真名 * * @return 真名 */ public String decryptRealName(String name) throws DecryptFailureException { return securityContext.decrypt(name, PRIVATE_KEY); } }