From 685d3ac646f090b3b7b8eef5837a45457fd296bf Mon Sep 17 00:00:00 2001
From: zhangli <123879394@qq.com>
Date: Sun, 27 Jul 2025 12:49:30 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1=E5=B0=8F?=
=?UTF-8?q?=E7=A8=8B=E5=BA=8F=E6=B3=A8=E5=86=8C=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 5 +
.../sczx/user/controller/AuthController.java | 12 +--
.../user/dto/WxMiniProgramRegRequest.java | 24 +++++
.../com/sczx/user/service/IUserService.java | 10 +-
.../user/service/impl/UserServiceImpl.java | 98 ++++++++++++++-----
.../dto/WechatDecryptedPhoneInfo.java | 20 ++++
.../dto/WechatDecryptedUserInfo.java | 21 ++++
.../dto/WechatEncryptedDataResponse.java | 14 +++
.../user/thirdpart/integ/WeichatInteg.java | 91 ++++++++++++++++-
9 files changed, 256 insertions(+), 39 deletions(-)
create mode 100644 src/main/java/com/sczx/user/dto/WxMiniProgramRegRequest.java
create mode 100644 src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedPhoneInfo.java
create mode 100644 src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedUserInfo.java
create mode 100644 src/main/java/com/sczx/user/thirdpart/dto/WechatEncryptedDataResponse.java
diff --git a/pom.xml b/pom.xml
index 7d1cbd1..bc703af 100644
--- a/pom.xml
+++ b/pom.xml
@@ -223,6 +223,11 @@
1.2.83
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.68
+
diff --git a/src/main/java/com/sczx/user/controller/AuthController.java b/src/main/java/com/sczx/user/controller/AuthController.java
index 241d9a4..6b99250 100644
--- a/src/main/java/com/sczx/user/controller/AuthController.java
+++ b/src/main/java/com/sczx/user/controller/AuthController.java
@@ -2,7 +2,7 @@ package com.sczx.user.controller;
import com.sczx.user.common.Result;
import com.sczx.user.dto.LoginResponse;
-import com.sczx.user.dto.RegReq;
+import com.sczx.user.dto.WxMiniProgramRegRequest;
import com.sczx.user.service.IUserService;
import com.sczx.user.util.JwtUtil;
import io.swagger.annotations.Api;
@@ -34,11 +34,11 @@ public class AuthController {
return Result.ok(phoneNumber);
}
- @ApiOperation(value = "小程序注册", notes = "注册")
- @PostMapping("/mini-program/register")
- public Result wechatRegister(
- @RequestBody RegReq regReq) {
- Boolean result = userService.miniProgramRegister(regReq.getOpenId(), regReq.getNickName(), regReq.getPhoneNumber(), regReq.getAvatarUrl());
+ @ApiOperation(value = "微信小程序注册", notes = "微信小程序注册")
+ @PostMapping("/mini-program/wechat/register")
+ public Result wechatRegister(
+ @RequestBody WxMiniProgramRegRequest wxMiniProgramRegRequest) {
+ LoginResponse result = userService.wxMiniProgramRegister(wxMiniProgramRegRequest);
return Result.ok(result);
}
diff --git a/src/main/java/com/sczx/user/dto/WxMiniProgramRegRequest.java b/src/main/java/com/sczx/user/dto/WxMiniProgramRegRequest.java
new file mode 100644
index 0000000..d83baaf
--- /dev/null
+++ b/src/main/java/com/sczx/user/dto/WxMiniProgramRegRequest.java
@@ -0,0 +1,24 @@
+package com.sczx.user.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@ApiModel(value = "微信小程序注册请求")
+@Data
+public class WxMiniProgramRegRequest {
+ @ApiModelProperty(value = "微信code")
+ String code;
+
+ @ApiModelProperty(value = "微信手机号加密数据")
+ String phoneEncryptedData;
+
+ @ApiModelProperty(value = "微信手机号加密数据iv")
+ String phoneIv;
+
+ @ApiModelProperty(value = "微信用户加密数据")
+ String userEncryptedData;
+
+ @ApiModelProperty(value = "微信手机号加密数据iv")
+ String userIv;
+}
diff --git a/src/main/java/com/sczx/user/service/IUserService.java b/src/main/java/com/sczx/user/service/IUserService.java
index 77ad33b..7615458 100644
--- a/src/main/java/com/sczx/user/service/IUserService.java
+++ b/src/main/java/com/sczx/user/service/IUserService.java
@@ -2,6 +2,7 @@ package com.sczx.user.service;
import com.sczx.user.dto.LoginResponse;
import com.sczx.user.dto.SimpleUserInfoDTO;
+import com.sczx.user.dto.WxMiniProgramRegRequest;
/**
* @Author: 张黎
@@ -25,14 +26,11 @@ public interface IUserService {
String getWxPhoneNumber(String phoneCode);
/**
- * 小程序注册
- * @param openId
- * @param userName
- * @param phoneNumber
- * @param avatarUrl
+ * 微信小程序注册
+ * @param wxMiniProgramRegRequest
* @return
*/
- Boolean miniProgramRegister(String openId, String userName,String phoneNumber,String avatarUrl);
+ LoginResponse wxMiniProgramRegister(WxMiniProgramRegRequest wxMiniProgramRegRequest);
/**
* 微信小程序登录
* @param code 微信登录code
diff --git a/src/main/java/com/sczx/user/service/impl/UserServiceImpl.java b/src/main/java/com/sczx/user/service/impl/UserServiceImpl.java
index bc56284..950f3f8 100644
--- a/src/main/java/com/sczx/user/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/sczx/user/service/impl/UserServiceImpl.java
@@ -6,20 +6,26 @@ import com.sczx.user.common.enums.MiniProgramTypeEnum;
import com.sczx.user.convert.UserInfoConvert;
import com.sczx.user.dto.LoginResponse;
import com.sczx.user.dto.SimpleUserInfoDTO;
+import com.sczx.user.dto.WxMiniProgramRegRequest;
import com.sczx.user.exception.BizException;
import com.sczx.user.po.BaseUserPO;
import com.sczx.user.repository.BaseUserRepo;
import com.sczx.user.service.IUserService;
+import com.sczx.user.thirdpart.dto.WechatDecryptedPhoneInfo;
+import com.sczx.user.thirdpart.dto.WechatDecryptedUserInfo;
+import com.sczx.user.thirdpart.dto.WechatMiniProgramResponse;
import com.sczx.user.thirdpart.integ.WeichatInteg;
import com.sczx.user.util.JwtUtil;
import com.sczx.user.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
/**
* @Author: 张黎
@@ -65,22 +71,57 @@ public class UserServiceImpl implements IUserService {
}
@Override
- public Boolean miniProgramRegister(String openId, String nickName, String phoneNumber, String avatarUrl) {
- LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(BaseUserPO::getPhoneNumber, phoneNumber);
- BaseUserPO baseUserPO = baseUserRepo.getOne(queryWrapper);
- if(Objects.nonNull(baseUserPO)){
- throw new BizException("该手机号用户已注册");
+ public LoginResponse wxMiniProgramRegister(WxMiniProgramRegRequest wxMiniProgramRegRequest) {
+ try{
+ // 1. 通过code获取session_key和openid
+ WechatMiniProgramResponse sessionInfo = weichatInteg.getSessionInfoByCode(wxMiniProgramRegRequest.getCode());
+ if (sessionInfo == null || sessionInfo.getOpenid() == null) {
+ throw new BizException("获取微信用户信息失败");
+ }
+
+ String openid = sessionInfo.getOpenid();
+ String sessionKey = sessionInfo.getSession_key();
+
+ // 2. 解密手机号数据
+ WechatDecryptedPhoneInfo phoneInfo = null;
+ if (StringUtils.isNotBlank(wxMiniProgramRegRequest.getPhoneEncryptedData()) && StringUtils.isNotBlank(wxMiniProgramRegRequest.getPhoneIv())
+ && StringUtils.isNotBlank(sessionKey)) {
+ phoneInfo = weichatInteg.decryptPhoneNumber(sessionKey, wxMiniProgramRegRequest.getPhoneEncryptedData(), wxMiniProgramRegRequest.getPhoneIv());
+ }
+
+ // 3. 解密用户基本信息(昵称、头像等)
+ WechatDecryptedUserInfo userInfo = null;
+ if (StringUtils.isNotBlank(wxMiniProgramRegRequest.getUserEncryptedData()) && StringUtils.isNotBlank(wxMiniProgramRegRequest.getUserIv())
+ && StringUtils.isNotBlank(sessionKey)) {
+ userInfo = weichatInteg.decryptUserInfo(sessionKey, wxMiniProgramRegRequest.getUserEncryptedData(), wxMiniProgramRegRequest.getUserIv());
+ }
+
+
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(BaseUserPO::getPhoneNumber, phoneInfo.getPurePhoneNumber());
+ BaseUserPO baseUserPO = baseUserRepo.getOne(queryWrapper);
+ BaseUserPO newUserPO = new BaseUserPO();
+ if(Objects.isNull(baseUserPO)){
+ newUserPO.setWechatOpenid(openid);
+ newUserPO.setUserName(phoneInfo.getPurePhoneNumber());
+ newUserPO.setPhoneNumber(phoneInfo.getPurePhoneNumber());
+ newUserPO.setPassword(MD5Utils.md5Hex("88888888", "UTF-8"));
+ newUserPO.setNickName(Optional.ofNullable(userInfo).map(WechatDecryptedUserInfo::getNickName).orElse(null));
+ newUserPO.setAvatarUrl(Optional.ofNullable(userInfo).map(WechatDecryptedUserInfo::getAvatarUrl).orElse(null));
+ newUserPO.setRoleId(1);
+ }else {
+ newUserPO.setId(baseUserPO.getId());
+ newUserPO.setWechatOpenid(openid);
+ newUserPO.setNickName(Optional.ofNullable(userInfo).map(WechatDecryptedUserInfo::getNickName).orElse(null));
+ newUserPO.setAvatarUrl(Optional.ofNullable(userInfo).map(WechatDecryptedUserInfo::getAvatarUrl).orElse(null));
+ }
+ baseUserRepo.saveOrUpdate(newUserPO);
+
+ return getLoginResponse(openid,MiniProgramTypeEnum.WECHAT.getType());
+ }catch (Exception e){
+ log.error("微信小程序注册失败", e);
+ throw new BizException("微信小程序注册失败");
}
- baseUserPO = new BaseUserPO();
- baseUserPO.setUserName(nickName);
- baseUserPO.setNickName(nickName);
- baseUserPO.setPhoneNumber(phoneNumber);
- baseUserPO.setPassword(MD5Utils.md5Hex("88888888", "UTF-8"));
- baseUserPO.setAvatarUrl(avatarUrl);
- baseUserPO.setRoleId(1);
- baseUserRepo.save(baseUserPO);
- return true;
}
@Override
@@ -89,15 +130,7 @@ public class UserServiceImpl implements IUserService {
if (openid == null) {
throw new BizException("无效的微信登录code");
}
- // 模拟登录逻辑
- SimpleUserInfoDTO simpleUserInfoDTO = getUserInfoByProgramId(openid, MiniProgramTypeEnum.WECHAT.getType());
-
- String token = jwtUtil.generateToken(simpleUserInfoDTO, simpleUserInfoDTO.getUserName());
-
- LoginResponse loginResponse = new LoginResponse();
- loginResponse.setToken(token);
- loginResponse.setUserInfo(simpleUserInfoDTO);
- return loginResponse;
+ return getLoginResponse(openid,MiniProgramTypeEnum.WECHAT.getType());
}
@@ -133,4 +166,21 @@ public class UserServiceImpl implements IUserService {
BaseUserPO baseUserPO = baseUserRepo.getOne(queryWrapper);
return UserInfoConvert.INSTANCE.poToSimpleDTO(baseUserPO);
}
+
+ /**
+ * 获取登录信息
+ * @param programId
+ * @param programType
+ * @return
+ */
+ private LoginResponse getLoginResponse(String programId, String programType) {
+ SimpleUserInfoDTO simpleUserInfoDTO = getUserInfoByProgramId(programId, programType);
+
+ String token = jwtUtil.generateToken(simpleUserInfoDTO, simpleUserInfoDTO.getUserName());
+
+ LoginResponse loginResponse = new LoginResponse();
+ loginResponse.setToken(token);
+ loginResponse.setUserInfo(simpleUserInfoDTO);
+ return loginResponse;
+ }
}
diff --git a/src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedPhoneInfo.java b/src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedPhoneInfo.java
new file mode 100644
index 0000000..7c14656
--- /dev/null
+++ b/src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedPhoneInfo.java
@@ -0,0 +1,20 @@
+package com.sczx.user.thirdpart.dto;
+
+import lombok.Data;
+
+/**
+ * 微信解密手机信息
+ */
+@Data
+public class WechatDecryptedPhoneInfo {
+ private String phoneNumber;
+ private String purePhoneNumber;
+ private String countryCode;
+ private Watermark watermark;
+
+ @Data
+ public static class Watermark {
+ private String appid;
+ private Long timestamp;
+ }
+}
diff --git a/src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedUserInfo.java b/src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedUserInfo.java
new file mode 100644
index 0000000..8c4bb99
--- /dev/null
+++ b/src/main/java/com/sczx/user/thirdpart/dto/WechatDecryptedUserInfo.java
@@ -0,0 +1,21 @@
+package com.sczx.user.thirdpart.dto;
+
+import lombok.Data;
+
+@Data
+public class WechatDecryptedUserInfo {
+ private String nickName;
+ private Integer gender;
+ private String language;
+ private String city;
+ private String province;
+ private String country;
+ private String avatarUrl;
+ private Watermark watermark;
+
+ @Data
+ public static class Watermark {
+ private String appid;
+ private Long timestamp;
+ }
+}
diff --git a/src/main/java/com/sczx/user/thirdpart/dto/WechatEncryptedDataResponse.java b/src/main/java/com/sczx/user/thirdpart/dto/WechatEncryptedDataResponse.java
new file mode 100644
index 0000000..9ec5daa
--- /dev/null
+++ b/src/main/java/com/sczx/user/thirdpart/dto/WechatEncryptedDataResponse.java
@@ -0,0 +1,14 @@
+package com.sczx.user.thirdpart.dto;
+
+import lombok.Data;
+
+/**
+ * 微信小程序返回的加密数据
+ */
+@Data
+public class WechatEncryptedDataResponse {
+ private String encryptedData;
+ private String iv;
+ private String signature;
+ private String rawData;
+}
\ No newline at end of file
diff --git a/src/main/java/com/sczx/user/thirdpart/integ/WeichatInteg.java b/src/main/java/com/sczx/user/thirdpart/integ/WeichatInteg.java
index 99a1cf1..3a48bf6 100644
--- a/src/main/java/com/sczx/user/thirdpart/integ/WeichatInteg.java
+++ b/src/main/java/com/sczx/user/thirdpart/integ/WeichatInteg.java
@@ -1,6 +1,9 @@
package com.sczx.user.thirdpart.integ;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.sczx.user.exception.BizException;
+import com.sczx.user.thirdpart.dto.WechatDecryptedPhoneInfo;
+import com.sczx.user.thirdpart.dto.WechatDecryptedUserInfo;
import com.sczx.user.thirdpart.dto.WechatMiniProgramResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
@@ -10,6 +13,10 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -42,7 +49,7 @@ public class WeichatInteg {
return objectMapper.readValue(response, WechatMiniProgramResponse.class);
} catch (Exception e) {
log.error("获取微信session信息异常", e);
- return null;
+ throw new BizException("获取微信session信息异常");
}
}
@@ -54,17 +61,95 @@ public class WeichatInteg {
public String getWechatOpenIdByCode(String code) {
WechatMiniProgramResponse response = getSessionInfoByCode(code);
if (response == null) {
- return null;
+ throw new BizException("获取微信openId信息异常");
}
if (response.getErrcode() != null && response.getErrcode() != 0) {
log.error("微信接口调用失败,错误码:{},错误信息:{}", response.getErrcode(), response.getErrmsg());
- return null;
+ throw new BizException("获取微信openId信息异常");
}
return response.getOpenid();
}
+ /**
+ * 解密微信加密数据
+ * @param sessionKey 会话密钥
+ * @param encryptedData 加密数据
+ * @param iv 初始化向量
+ * @return 解密后的数据字符串
+ */
+ public String decryptWechatData(String sessionKey, String encryptedData, String iv) {
+ try {
+ byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
+ byte[] encryptedDataBytes = Base64.getDecoder().decode(encryptedData);
+ byte[] ivBytes = Base64.getDecoder().decode(iv);
+
+ Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+ SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES");
+ IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+
+ byte[] result = cipher.doFinal(encryptedDataBytes);
+ String resultStr = new String(result);
+
+ // 去除填充字符
+ int paddingIndex = resultStr.indexOf("\0");
+ if (paddingIndex > 0) {
+ resultStr = resultStr.substring(0, paddingIndex);
+ }
+
+ return resultStr;
+ } catch (Exception e) {
+ log.error("解密微信数据异常", e);
+ throw new BizException("解密微信数据异常");
+ }
+ }
+
+ /**
+ * 解密手机号信息
+ * @param sessionKey 会话密钥
+ * @param encryptedData 加密的手机号数据
+ * @param iv 初始化向量
+ * @return 解密后的手机号信息
+ */
+ public WechatDecryptedPhoneInfo decryptPhoneNumber(String sessionKey, String encryptedData, String iv) {
+ try {
+ String decryptedJson = decryptWechatData(sessionKey, encryptedData, iv);
+ if (decryptedJson == null) {
+ return null;
+ }
+
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.readValue(decryptedJson, WechatDecryptedPhoneInfo.class);
+ } catch (Exception e) {
+ log.error("解密手机号信息异常", e);
+ throw new BizException("解密手机号信息异常");
+ }
+ }
+
+ /**
+ * 解密用户信息(昵称、头像等)
+ * @param sessionKey 会话密钥
+ * @param encryptedData 加密的用户数据
+ * @param iv 初始化向量
+ * @return 解密后的用户信息
+ */
+ public WechatDecryptedUserInfo decryptUserInfo(String sessionKey, String encryptedData, String iv) {
+ try {
+ String decryptedJson = decryptWechatData(sessionKey, encryptedData, iv);
+ if (decryptedJson == null) {
+ return null;
+ }
+
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.readValue(decryptedJson, WechatDecryptedUserInfo.class);
+ } catch (Exception e) {
+ log.error("解密用户信息异常", e);
+ throw new BizException("解密用户信息异常");
+ }
+ }
+
/**
* 获取访问令牌(用于调用其他微信接口)
* @return access_token