321 lines
13 KiB
Java
Raw Normal View History

2025-10-15 17:38:13 +08:00
package com.cdzy.user.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
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;
2025-10-16 11:39:47 +08:00
import com.cdzy.user.enums.RealNameAttestationStatus;
2025-10-15 17:38:13 +08:00
import com.cdzy.user.model.dto.UserValidateDto;
import com.cdzy.user.model.entity.EbikeUser;
import com.cdzy.user.model.entity.EbikeUserRealInfo;
import com.cdzy.user.service.EbikeUserRealInfoService;
import com.cdzy.user.service.EbikeUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Service;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
2025-10-21 09:57:21 +08:00
import java.io.IOException;
2025-10-15 17:38:13 +08:00
import java.nio.charset.StandardCharsets;
2025-10-21 09:57:21 +08:00
import java.security.Security;
2025-10-15 17:38:13 +08:00
import java.time.LocalDateTime;
import java.util.*;
/**
* 验证工具类
*
2025-10-21 09:57:21 +08:00
* @author: yanglei
* @since: 2025-10-15 09:30
2025-10-15 17:38:13 +08:00
*/
@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;
//暂时硬编码后续放到redis或者config中【本地数据加密解密】
private SecureComponent secureComponent;
2025-10-21 09:57:21 +08:00
private static final String PRIVATE_KEY = "f8209a2ebe6691e41e1f2b667bfe71f0b511716cc0f7c4452502fc12ec3957e4";
private static final String PUBLIC_KEY = "04f5084ee12767d932f293508e30e3b0100185042ec0f061dedaf92b793b93f79fd6179d5e47e25b7aec98e00cf90dd56df1f8191012537187e7bbfd2d1de299fc";
2025-10-15 17:38:13 +08:00
// 加解密上下⽂
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 JSONObject wechatAuthority(String code) {
Map<String, String> 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 验证结果
*/
public JsonResult<?> verifyRealName(UserValidateDto userValidateDto) {
EbikeUser ebikeUser = ebikeUserService.getById(userValidateDto.getUserId());
if (ebikeUser == null) {
log.error("用户不存在");
return JsonResult.failed("用户不存在");
}
try {
// 1. 加密数据
Map<String, String> params = new HashMap<>();
params.put("name", securityContext.encrypt(userValidateDto.getName(), realNameVerifyConfig.getServerPublicKey()));
params.put("idcard", securityContext.encrypt(userValidateDto.getIdCard(), realNameVerifyConfig.getServerPublicKey()));
params.put("timestamp", securityContext.encrypt(String.valueOf(System.currentTimeMillis()), realNameVerifyConfig.getServerPublicKey()));
String sign = securityContext.sign(params, realNameVerifyConfig.getClientPrivateKey());
params.put("sign", sign);
params.put("key", realNameVerifyConfig.getApiKey());
// 2. 验证用户实名
JSONObject result = httpPost("验证用户实名", REALNAME_VERIFY_URL, params, null);
log.info("验证用户实名结果:{}", result);
if (result == null) {
log.error("验证用户实名失败");
return JsonResult.failed("验证用户实名失败");
}
if ("10000".equals(result.getString("code"))) {
// 3. 解密结果
String data = securityContext.decrypt(result.getString("data"), realNameVerifyConfig.getClientPrivateKey());
JSONObject jsonData = JSON.parseObject(data);
if ("1".equals(jsonData.getString("result"))) {
// 4. 更新用户实名信息
updateRealNameInfo(userValidateDto, true);
return JsonResult.success("验证用户实名成功");
}
}
updateRealNameInfo(userValidateDto, false);
log.error("验证用户实名失败,{} {}", result.getString("code"), result.getString("message"));
return JsonResult.failed(result.getString("code") + ", " + result.getString("message"));
} 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("结果解密失败");
}
}
/**
* 更新用户实名信息
*
* @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());
2025-10-21 09:57:21 +08:00
ebikeUserRealinfo.setUserRealName(getSecureComponent().encrypt(realName, PUBLIC_KEY));
ebikeUserRealinfo.setUserIdCard(getSecureComponent().encrypt(idCard, PUBLIC_KEY));
2025-10-16 11:39:47 +08:00
ebikeUserRealinfo.setAttestationStatus(isValid ? RealNameAttestationStatus.CERTIFIED : RealNameAttestationStatus.PENDING_REVIEW);
2025-10-15 17:38:13 +08:00
ebikeUserRealinfo.setAuditTime(LocalDateTime.now());
ebikeUserRealInfoService.save(ebikeUserRealinfo);
} else {
2025-10-21 09:57:21 +08:00
ebikeUserRealinfo.setUserRealName(getSecureComponent().encrypt(realName, PUBLIC_KEY));
ebikeUserRealinfo.setUserIdCard(getSecureComponent().encrypt(idCard, PUBLIC_KEY));
ebikeUserRealinfo.setAttestationStatus(isValid ? RealNameAttestationStatus.CERTIFIED : RealNameAttestationStatus.PENDING_REVIEW);
2025-10-15 17:38:13 +08:00
ebikeUserRealinfo.setAuditTime(LocalDateTime.now());
ebikeUserRealInfoService.updateById(ebikeUserRealinfo);
}
}
/**
* 发送HTTP GET请求
*
* @param func_ 功能描述
* @param url 请求URL
* @param params 请求参数
* @return 响应结果
*/
private JSONObject httpGet(String func_, String url, Map<String, String> params) {
StringJoiner paramJoiner = new StringJoiner("&");
for (Map.Entry<String, String> entry : params.entrySet()) {
paramJoiner.add(entry.getKey() + "=" + entry.getValue());
}
String requestUrl = url + "?" + paramJoiner.toString();
Request request = new Request.Builder()
.url(requestUrl)
.get()
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
if (response.body() != null) {
return JSON.parseObject(response.body().string());
}
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;
}
}
/**
* 发送HTTP POST参数请求
*
* @param func_ 功能描述
* @param url 请求URL
* @param params 请求参数
* @return 响应结果
*/
private JSONObject httpPost(String func_, String url, Map<String, String> params, Map<String, Object> body) {
StringJoiner paramJoiner = new StringJoiner("&");
for (Map.Entry<String, String> entry : params.entrySet()) {
paramJoiner.add(entry.getKey() + "=" + entry.getValue());
}
String requestUrl = url;
if (paramJoiner.length() > 0)
requestUrl += "?" + paramJoiner.toString();
Request.Builder builder = new Request.Builder()
.url(requestUrl);
if (body != null) {
MediaType typeJson = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody = RequestBody.create(JSON.toJSONString(body), typeJson);
builder.post(requestBody);
} else {
builder.post(RequestBody.create(null, new byte[0]));
}
Request request = builder.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
if (response.body() != null) {
return JSON.parseObject(response.body().string());
}
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 SecureComponent getSecureComponent() {
if (Objects.isNull(secureComponent)) {
secureComponent = new SecureSM2Component();
}
return secureComponent;
}
/**
* 解密用户真名
*
* @return 真名
*/
public String decryptRealName(String name) throws DecryptFailureException {
2025-10-21 09:57:21 +08:00
return securityContext.decrypt(name, PRIVATE_KEY);
2025-10-15 17:38:13 +08:00
}
}