Browse Source

sm4和md5 加解密 适配

master
liwenxuan 2 months ago
parent
commit
2769f76fc4
  1. 17
      pom.xml
  2. 74
      src/main/java/com/dreamchaser/depository_manage/config/PublicConfig.java
  3. 11
      src/main/java/com/dreamchaser/depository_manage/controller/CompanyController.java
  4. 36
      src/main/java/com/dreamchaser/depository_manage/controller/IdentityVerifyController.java
  5. 7
      src/main/java/com/dreamchaser/depository_manage/controller/PageController.java
  6. 6
      src/main/java/com/dreamchaser/depository_manage/controller/UserController.java
  7. 26
      src/main/java/com/dreamchaser/depository_manage/service/impl/CompanyServiceImpl.java
  8. 3
      src/main/java/com/dreamchaser/depository_manage/service/impl/DepositoryRecordServiceImpl.java
  9. 32
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/ApiResponse.java
  10. 45
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedArgumentResolver.java
  11. 9
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedBody.java
  12. 20
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedRequest.java
  13. 113
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedResponseAdvice.java
  14. 33
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/ErrorCode.java
  15. 22
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/GlobalExceptionHandler.java
  16. 372
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoApiClient.java
  17. 69
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoMd5EncryptionUtil.java
  18. 47
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/RandomStringUtil.java
  19. 98
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Interceptor.java
  20. 217
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Utils.java
  21. 40
      src/main/java/com/dreamchaser/depository_manage/utils/sm4/WebConfig.java
  22. 13
      src/main/resources/application-prod.yml

17
pom.xml

@ -266,6 +266,23 @@
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.46</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.76</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
</dependencies>

74
src/main/java/com/dreamchaser/depository_manage/config/PublicConfig.java

@ -8,8 +8,9 @@ import com.dreamchaser.depository_manage.entity.Post;
import com.dreamchaser.depository_manage.entity.UserByPort;
import com.dreamchaser.depository_manage.pojo.AdministrationP;
import com.dreamchaser.depository_manage.utils.HttpUtils;
import com.dreamchaser.depository_manage.utils.Md5;
import com.dreamchaser.depository_manage.utils.ObjectFormatUtil;
import com.dreamchaser.depository_manage.utils.sm4.GoApiClient;
import com.dreamchaser.depository_manage.utils.sm4.GoMd5EncryptionUtil;
import lombok.Data;
import org.apache.http.protocol.HTTP;
@ -24,6 +25,7 @@ import java.util.concurrent.TimeUnit;
*/
@Data
public class PublicConfig {
private static GoApiClient goApiClient = new GoApiClient();
// 部署数据库 depository NhE47edekBHxhjYk
@ -84,7 +86,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -124,7 +126,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -162,6 +164,10 @@ public class PublicConfig {
*/
public static Map<String,String> getWaiKeyNumber(){
/*
* */
String url = "http://172.20.2.87:39168/empower/gaintoken";
Map<String, Object> map = new HashMap<>();
@ -184,7 +190,7 @@ public class PublicConfig {
String codeString = data.get("number").toString();
String token = data.get("token").toString();
String appKey = "luquan_hr";
/*String appKey = "luquan_hr";
String one1 = Md5.crypt(codeString);
String one2 = Md5.crypt(appKey);
@ -197,7 +203,9 @@ public class PublicConfig {
String therr = Md5.crypt(two);
System.out.println("therr");
System.out.println(therr);
String number = therr;
String number = therr;*/
GoMd5EncryptionUtil util = new GoMd5EncryptionUtil();
String number = util.md5EncryptionAlgorithm(codeString);
Map<String,String> map1 = new HashMap<>();
map1.put("token",token);
map1.put("number",number);
@ -224,7 +232,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send2(url, paramObject, HTTP.UTF_8, TokenNumberMap.get("token"), TokenNumberMap.get("number"));
post = goApiClient.send2(url, paramObject, HTTP.UTF_8, TokenNumberMap.get("token"), TokenNumberMap.get("number"));
} catch (IOException e) {
e.printStackTrace();
}
@ -251,7 +259,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -264,13 +272,19 @@ public class PublicConfig {
map.put("id", mainPortId);
map.put("idstr", String.valueOf(mainPortId));
String jsonString = JSONObject.toJSONString(map);
String s = HttpUtils.doPost(url, jsonString);
JSONObject jsonObject = JSONObject.parseObject(s);
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
Map<String,String> TokenNumberMap = getWaiKeyNumber();
post = goApiClient.send2(url, paramObject, HTTP.UTF_8, TokenNumberMap.get("token"), TokenNumberMap.get("number"));
} catch (IOException e) {
e.printStackTrace();
}
JSONObject jsonObject = JSONObject.parseObject(post);
JSONObject data = jsonObject.getJSONObject("data");
return data;
}
/**
* 通过工号获取用户
*
@ -282,8 +296,15 @@ public class PublicConfig {
Map<String, Object> map = new HashMap<>();
map.put("number", number);
String jsonString = JSONObject.toJSONString(map);
String s = HttpUtils.doPost(url, jsonString);
JSONObject jsonObject = JSONObject.parseObject(s);
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
Map<String,String> TokenNumberMap = getWaiKeyNumber();
post = goApiClient.send2(url, paramObject, HTTP.UTF_8, TokenNumberMap.get("token"), TokenNumberMap.get("number"));
} catch (IOException e) {
e.printStackTrace();
}
JSONObject jsonObject = JSONObject.parseObject(post);
JSONObject data = jsonObject.getJSONObject("data");
JSONArray list = data.getJSONArray("list");
UserByPort userByPort = null;
@ -349,7 +370,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -383,8 +404,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -414,8 +434,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -448,7 +467,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -474,7 +493,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -508,8 +527,11 @@ public class PublicConfig {
String url = external_url_6666 + "/base/captcha";
JSONObject param = new JSONObject();
String post = null;
try {
post = HttpUtils.send(url, param, HTTP.UTF_8, userKey, token);
// 使用GoApiClient发送加密请求
post = goApiClient.callGoApi(url, param, userKey, token);
System.out.println("post:"+post.toString());
} catch (IOException e) {
e.printStackTrace();
}
@ -534,7 +556,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -574,13 +596,12 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
JSONObject jsonObject = JSONObject.parseObject(post);
return (JSONArray) jsonObject.get("data");
}
/**
@ -599,7 +620,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -624,7 +645,7 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, userToken);
post = goApiClient.callGoApi(url, paramObject, userKey, userToken);
} catch (IOException e) {
e.printStackTrace();
}
@ -645,7 +666,8 @@ public class PublicConfig {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, null, null);
Map<String,String> TokenNumberMap = getWaiKeyNumber();
post = goApiClient.send2(url, paramObject, HTTP.UTF_8, TokenNumberMap.get("token"), TokenNumberMap.get("number"));
} catch (IOException e) {
e.printStackTrace();
}

11
src/main/java/com/dreamchaser/depository_manage/controller/CompanyController.java

@ -9,7 +9,7 @@ import com.dreamchaser.depository_manage.pojo.*;
import com.dreamchaser.depository_manage.security.pool.AuthenticationTokenPool;
import com.dreamchaser.depository_manage.service.*;
import com.dreamchaser.depository_manage.utils.*;
import org.apache.http.protocol.HTTP;
import com.dreamchaser.depository_manage.utils.sm4.GoApiClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -43,8 +43,7 @@ public class CompanyController {
@Autowired
private PostService postService;
private static GoApiClient goApiClient = new GoApiClient();
/**
* 查询组织架构
@ -79,7 +78,7 @@ public class CompanyController {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8,key, token);
post = goApiClient.callGoApi(url, paramObject, key, token);
} catch (IOException e) {
e.printStackTrace();
}
@ -165,7 +164,7 @@ public class CompanyController {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, key,token);
post = goApiClient.callGoApi(url, paramObject, key, token);
} catch (IOException e) {
e.printStackTrace();
}
@ -208,7 +207,7 @@ public class CompanyController {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, key,token);
post = goApiClient.callGoApi(url, paramObject, key, token);
} catch (IOException e) {
e.printStackTrace();
}

36
src/main/java/com/dreamchaser/depository_manage/controller/IdentityVerifyController.java

@ -7,19 +7,14 @@ import com.dreamchaser.depository_manage.security.pool.AuthenticationTokenPool;
import com.dreamchaser.depository_manage.security.pool.RedisPool;
import com.dreamchaser.depository_manage.service.UserService;
import com.dreamchaser.depository_manage.utils.EncryptionAlgorithmUtil;
import com.dreamchaser.depository_manage.utils.HttpUtils;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j;
import com.dreamchaser.depository_manage.utils.sm4.GoApiClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.protocol.HTTP;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@ -34,25 +29,26 @@ import java.util.Map;
@Slf4j
@RestController
public class IdentityVerifyController {
private static GoApiClient goApiClient = new GoApiClient();
@Autowired
private RedisPool redisPool;
@Autowired
private UserService userService;
/**
* 用于验证用户身份
*/
@GetMapping("/identityVerify")
public ModelAndView identityVerify(@RequestParam Map<String,String> map, HttpServletRequest request, HttpServletResponse response){
public ModelAndView identityVerify(@RequestParam Map<String, String> map, HttpServletRequest request, HttpServletResponse response) {
ModelAndView mv = new ModelAndView();
mv.addObject("userWxId", "");
mv.setViewName("pages/user/login");
String url = PublicConfig.external_url_extranet+"/signcode/signcode";
String url = PublicConfig.external_url_extranet + "/signcode/signcode";
String userkey = map.get("userkey");
String usertoken = map.get("usertoken");
@ -60,45 +56,45 @@ public class IdentityVerifyController {
EncryptionAlgorithmUtil encryptionAlgorithmUtil = new EncryptionAlgorithmUtil(code);
// 获取加密后的字符串
String algorithm = encryptionAlgorithmUtil.Algorithm();
Map<String,String> paramForPost = new HashMap<>();
paramForPost.put("code",algorithm);
Map<String, String> paramForPost = new HashMap<>();
paramForPost.put("code", algorithm);
String jsonString = JSONObject.toJSONString(paramForPost);
JSONObject paramObject = JSONObject.parseObject(jsonString);
try {
String send = HttpUtils.send(url, paramObject, HTTP.UTF_8, userkey, usertoken);
// 使用 GoApiClient 发送不加密请求,但解密响应
String send = goApiClient.sendNoEncryptRequest(url, paramObject, userkey, usertoken);
JSONObject resultObj = JSONObject.parseObject(send);
int resultCode = resultObj.getInteger("code");
if(resultCode == 0){
if (resultCode == 0) {
// 如果验证成功
String redisKey = PublicConfig.LoginRedisPrefix + userkey;
Object usernumber = redisPool.getRedisTemplateByDb(5).opsForHash().get(redisKey, "usernumber");
if(usernumber != null){
if (usernumber != null) {
UserByPort userByNumber = userService.findUserByNumber(String.valueOf(usernumber));
// 设置放入时间
userByNumber.setInstant(Instant.now());
// 将登录用户存储到池中保存
AuthenticationTokenPool.addUserToken(usertoken,userByNumber);
AuthenticationTokenPool.addUserToken(usertoken, userByNumber);
// 将key与token设置到session中
request.getSession().setAttribute("userKey", userkey);
request.getSession().setAttribute("userToken", usertoken);
request.getSession().setMaxInactiveInterval(3 * 60 * 60);
try {
response.sendRedirect("http://localhost:11111");
} catch (IOException e) {
e.printStackTrace();
}
}else{
} else {
JSONObject captcha = PublicConfig.Captcha(request);
String picPath = (String) captcha.get("picPath");
String captchaid = (String) captcha.get("captchaid");
mv.addObject("picPath", picPath);
mv.addObject("captchaid", captchaid);
}
}else{
} else {
// 如果验证失败
JSONObject captcha = PublicConfig.Captcha(request);
String picPath = (String) captcha.get("picPath");

7
src/main/java/com/dreamchaser/depository_manage/controller/PageController.java

@ -12,6 +12,7 @@ import com.dreamchaser.depository_manage.security.pool.AuthenticationTokenPool;
import com.dreamchaser.depository_manage.security.pool.RedisPool;
import com.dreamchaser.depository_manage.service.*;
import com.dreamchaser.depository_manage.utils.*;
import com.dreamchaser.depository_manage.utils.sm4.GoApiClient;
import org.apache.http.protocol.HTTP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@ -35,7 +36,7 @@ import java.util.*;
*/
@Controller
public class PageController {
private static GoApiClient goApiClient = new GoApiClient();
@Autowired
private MaterialTypeService materialTypeService;
@Autowired
@ -3244,7 +3245,7 @@ public class PageController {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, token);
post = goApiClient.callGoApi(url, paramObject, userKey, token);
} catch (IOException e) {
e.printStackTrace();
}
@ -3315,7 +3316,7 @@ public class PageController {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userKey, token);
post = goApiClient.callGoApi(url, paramObject, userKey, token);
} catch (IOException e) {
e.printStackTrace();
}

6
src/main/java/com/dreamchaser/depository_manage/controller/UserController.java

@ -14,6 +14,7 @@ import com.dreamchaser.depository_manage.service.AccessAddressService;
import com.dreamchaser.depository_manage.service.DepositoryService;
import com.dreamchaser.depository_manage.service.RoleService;
import com.dreamchaser.depository_manage.utils.*;
import com.dreamchaser.depository_manage.utils.sm4.GoApiClient;
import org.apache.http.protocol.HTTP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -35,7 +36,7 @@ import static com.dreamchaser.depository_manage.utils.CrudUtil.deleteHandle;
*/
@RestController
public class UserController {
private static GoApiClient goApiClient = new GoApiClient();
@Autowired
private RoleService roleService;
@ -149,7 +150,8 @@ public class UserController {
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url, paramObject, HTTP.UTF_8, userkey, token);
// 使用 GoApiClient 发送加密请求
post = goApiClient.callGoApi(url, paramObject, userkey, token);
} catch (IOException e) {
e.printStackTrace();
}

26
src/main/java/com/dreamchaser/depository_manage/service/impl/CompanyServiceImpl.java

@ -10,6 +10,7 @@ import com.dreamchaser.depository_manage.depository_mapper.CompanyMapper;
import com.dreamchaser.depository_manage.depository_mapper.ConstructionUnitMapper;
import com.dreamchaser.depository_manage.service.CompanyService;
import com.dreamchaser.depository_manage.utils.HttpUtils;
import com.dreamchaser.depository_manage.utils.sm4.GoApiClient;
import org.apache.http.protocol.HTTP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -20,7 +21,7 @@ import java.util.concurrent.*;
@Service
public class CompanyServiceImpl implements CompanyService {
private static GoApiClient goApiClient = new GoApiClient();
@Autowired
CompanyMapper companyMapper;
@ -542,27 +543,32 @@ public class CompanyServiceImpl implements CompanyService {
/**
* 用于获取行政组织树包括成员
* @param id 查询id
* @param
* @param userKey 用户key
* @param token 用户token
* @return
*/
public List<ThreeAboutMan> findAllUserByAdministration(String id,String userKey,String token){
String url = PublicConfig.external_url +"/org/govthreeaboutman";
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("level",0);
map.put("all",1);
public List<ThreeAboutMan> findAllUserByAdministration(String id, String userKey, String token) {
// 如果 GoApiClient 没有作为参数传入,需要注入
GoApiClient goApiClient = new GoApiClient();
String url = PublicConfig.external_url + "/org/govthreeaboutman";
Map<String, Object> map = new HashMap<>();
map.put("id", id);
map.put("level", 0);
map.put("all", 1);
String jsonString = JSONObject.toJSONString(map);
JSONObject paramObject = JSONObject.parseObject(jsonString);
String post = null;
try {
post = HttpUtils.send(url,paramObject, HTTP.UTF_8,userKey,token);
// 使用 GoApiClient 发送加密请求
post = goApiClient.callGoApi(url, paramObject, userKey, token);
} catch (IOException e) {
e.printStackTrace();
}
JSONObject jsonObject = JSONObject.parseObject(post);
JSONObject data = (JSONObject) jsonObject.get("data");
JSONArray list = (JSONArray) data.get("three");
if(list == null){
if (list == null) {
list = new JSONArray();
}
List<ThreeAboutMan> threeAboutManList = new ArrayList<>();

3
src/main/java/com/dreamchaser/depository_manage/service/impl/DepositoryRecordServiceImpl.java

@ -5860,6 +5860,9 @@ public class DepositoryRecordServiceImpl implements DepositoryRecordService {
} else {
departmenthead = userService.findUserByIdForNoPack(departmentheadId);
Administration departmentByUser = administrativeService.getDepartmentByUser(departmenthead);
if(null == departmentByUser){
continue;
}
departmenthead.setMaindeparmentname(departmentByUser.getName());
userByPortMap.put(departmentheadId, departmenthead);
}

32
src/main/java/com/dreamchaser/depository_manage/utils/sm4/ApiResponse.java

@ -0,0 +1,32 @@
package com.dreamchaser.depository_manage.utils.sm4;
import lombok.Data;
@Data
public class ApiResponse<T> {
private int code;
private String msg;
private T data;
public ApiResponse() {}
public ApiResponse(int code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static <T> ApiResponse<T> success(T data) {
if(null==data){
return new ApiResponse<T>(0, "成功", (T) "成功,没有相关数据");
}else{
return new ApiResponse<>(0, "成功", data);
}
}
public static <T> ApiResponse<T> error(int code, String msg) {
return new ApiResponse<>(code, msg, null);
}
}

45
src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedArgumentResolver.java

@ -0,0 +1,45 @@
package com.dreamchaser.depository_manage.utils.sm4;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@Component
public class DecryptedArgumentResolver implements HandlerMethodArgumentResolver {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(DecryptedBody.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String decryptedBody = (String) request.getAttribute("decryptedBody");
if (decryptedBody != null) {
log.debug("准备解析解密后的数据: {}", decryptedBody);
return objectMapper.readValue(decryptedBody, parameter.getParameterType());
}
// 如果没有解密数据,尝试直接解析请求体(非加密模式)
try {
return objectMapper.readValue(request.getInputStream(), parameter.getParameterType());
} catch (Exception e) {
log.warn("无法解析请求体为 {},返回null", parameter.getParameterType().getSimpleName());
return null;
}
}
}

9
src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedBody.java

@ -0,0 +1,9 @@
package com.dreamchaser.depository_manage.utils.sm4;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DecryptedBody {
}

20
src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedRequest.java

@ -0,0 +1,20 @@
package com.dreamchaser.depository_manage.utils.sm4;
import lombok.Data;
@Data
public class EncryptedRequest {
/**
* 加密的数据字段
* 前端发送的是 "data" 字段不是 "encryptedFile"
*/
private String data;
public EncryptedRequest() {}
public EncryptedRequest(String data) {
this.data = data;
}
}

113
src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedResponseAdvice.java

@ -0,0 +1,113 @@
package com.dreamchaser.depository_manage.utils.sm4;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@Component
@ControllerAdvice
public class EncryptedResponseAdvice implements ResponseBodyAdvice<Object> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
// 检查是否应该加密(可以根据路径或其他条件判断)
boolean shouldEncrypt = shouldEncryptResponse(servletRequest);
if (!shouldEncrypt) {
return body;
}
// 获取或生成 Auth-key
String authKey = (String) servletRequest.getAttribute("responseAuthKey");
if (authKey == null) {
authKey = RandomStringUtil.generateRandomString(16);
servletRequest.setAttribute("responseAuthKey", authKey);
}
// 设置响应头
response.getHeaders().set("Auth-key", authKey);
try {
// 只有 ApiResponse 才需要加密
if (body instanceof ApiResponse) {
ApiResponse<?> apiResponse = (ApiResponse<?>) body;
// 如果数据不为空,进行加密
if (apiResponse.getData() != null) {
// 将数据转换为 JSON 字符串
String jsonData = objectMapper.writeValueAsString(apiResponse.getData());
log.debug("准备加密的数据: {}", jsonData);
log.debug("使用的 Auth-key: {}", authKey);
// 使用 SM4 加密
String encryptedData = SM4Utils.encrypt(jsonData, authKey);
log.debug("加密结果长度: {}", encryptedData.length());
// 返回格式:{ code: 0, msg: "成功", data: "加密字符串" }
return new ApiResponse<>(apiResponse.getCode(), apiResponse.getMsg(), encryptedData);
} else {
// 数据为空,直接返回原始响应
return apiResponse;
}
}
// 如果不是 ApiResponse,直接返回(如文件下载等)
return body;
} catch (Exception e) {
log.error("响应加密失败", e);
// 加密失败时返回错误响应
return ApiResponse.error(500, "服务器加密错误: " + e.getMessage());
}
}
/**
* 判断是否应该加密响应
*/
private boolean shouldEncryptResponse(HttpServletRequest request) {
String path = request.getRequestURI();
// 可以根据路径排除某些接口
if (path.contains("/api/debug/") ||
path.contains("/actuator/") ||
path.contains("/swagger") ||
path.contains("/v2/api-docs") ||
path.endsWith(".html") ||
path.endsWith(".js") ||
path.endsWith(".css")) {
return false;
}
// 检查是否有特殊的请求头表示不需要加密
if ("false".equalsIgnoreCase(request.getHeader("X-No-Encrypt"))) {
return false;
}
return true;
}
}

33
src/main/java/com/dreamchaser/depository_manage/utils/sm4/ErrorCode.java

@ -0,0 +1,33 @@
package com.dreamchaser.depository_manage.utils.sm4;
public enum ErrorCode {
SUCCESS(0, "成功"),
DATA_FORMAT_ERROR(100, "提交的数据格式错误!"),
DATA_EMPTY(101, "提交的数据不能为空!"),
KEY_PARAM_EMPTY(102, "关键参数不能为空!"),
DATA_EXISTS(103, "该数据已经存在!请不要重复添加"),
WRITE_FAILED(104, "数据写入失败!"),
QUERY_FAILED(105, "数据查询失败!"),
UPDATE_FAILED(106, "编辑失败!"),
NO_DATA(107, "未能查询到数据!"),
DELETE_FAILED(108, "删除失败"),
AUTH_ERROR(2000, "账户或密码错误!");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}

22
src/main/java/com/dreamchaser/depository_manage/utils/sm4/GlobalExceptionHandler.java

@ -0,0 +1,22 @@
package com.dreamchaser.depository_manage.utils.sm4;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ApiResponse<Object> handleException(Exception e) {
log.error("系统异常:", e);
return ApiResponse.error(500, "服务器错误");
}
@ExceptionHandler(IllegalArgumentException.class)
public ApiResponse<Object> handleIllegalArgumentException(IllegalArgumentException e) {
return ApiResponse.error(400, e.getMessage());
}
}

372
src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoApiClient.java

@ -0,0 +1,372 @@
package com.dreamchaser.depository_manage.utils.sm4;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
public class GoApiClient {
private static final int TIMEOUT = 5000;
/**
* 调用 Go 接口使用加密通信
* @param url Go接口地址
* @param param 请求参数
* @param userKey User-Key
* @param userToken User-Token
* @return 解密后的响应字符串
* @throws IOException
*/
public String callGoApi(String url, Object param, String userKey, String userToken) throws IOException {
// 1. 生成随机 Auth-key(16位)
String authKey = RandomStringUtil.generateRandomString(16);
// 2. 加密请求数据
String encryptedData;
try {
String jsonParam = JSON.toJSONString(param);
encryptedData = SM4Utils.encrypt(jsonParam, authKey);
} catch (Exception e) {
log.error("加密请求数据失败", e);
throw new IOException("加密请求数据失败: " + e.getMessage(), e);
}
// 3. 构建加密请求体
Map<String, String> encryptedBody = new HashMap<>();
encryptedBody.put("data", encryptedData); // Go后端期望data字段
String requestBody = JSON.toJSONString(encryptedBody);
// 4. 发送 HTTP 请求
CloseableHttpClient httpClient = null;
try {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.build();
httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build();
HttpPost httpPost = new HttpPost(url);
// 设置请求头
httpPost.setHeader("User-Key", userKey);
httpPost.setHeader("User-Token", userToken);
httpPost.setHeader("Auth-key", authKey);
httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
// 设置请求体
StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
httpPost.setEntity(entity);
// 执行请求
HttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
String errorResponse = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
throw new IOException("HTTP 请求失败,状态码: " + statusCode + ", 响应: " + errorResponse);
}
// 获取响应体
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
// 获取响应头中的 Auth-key
org.apache.http.Header[] authKeyHeaders = response.getHeaders("Auth-key");
String responseAuthKey = null;
if (authKeyHeaders != null && authKeyHeaders.length > 0) {
responseAuthKey = authKeyHeaders[0].getValue();
}
// 5. 解密响应数据
if (responseAuthKey != null && !responseAuthKey.isEmpty()) {
return decryptGoResponse(responseBody, responseAuthKey);
} else {
log.warn("响应头中没有找到 Auth-key,返回原始响应");
return responseBody;
}
} catch (Exception e) {
log.error("HTTP 请求异常", e);
throw new IOException("HTTP 请求异常: " + e.getMessage(), e);
} finally {
if (httpClient != null) {
try {
httpClient.close();
} catch (IOException e) {
log.error("关闭 HTTP 客户端失败", e);
}
}
}
}
/**
* 解密 Go 后端的响应
*/
private String decryptGoResponse(String responseBody, String authKey) throws Exception {
try {
// 解析 Go 的响应
JSONObject responseJson = JSON.parseObject(responseBody);
int code = responseJson.getIntValue("code");
String msg = responseJson.getString("msg");
String encryptedData = responseJson.getString("data");
if (encryptedData != null && !encryptedData.isEmpty()) {
// 解密数据
String decryptedData = SM4Utils.decrypt(encryptedData, authKey);
// 重新构建响应
JSONObject result = new JSONObject();
result.put("code", code);
result.put("msg", msg);
try {
// 尝试将解密后的字符串解析为 JSON 对象
Object dataObj = JSON.parse(decryptedData);
result.put("data", dataObj);
} catch (Exception e) {
// 如果解析失败,直接使用字符串
result.put("data", decryptedData);
}
return result.toJSONString();
} else {
return responseBody;
}
} catch (Exception e) {
log.error("解密 Go 响应数据失败", e);
throw new Exception("解密 Go 响应数据失败: " + e.getMessage(), e);
}
}
/**
* 调用 Go 接口使用加密通信- 支持自定义 headers
* @param url Go接口地址
* @param param 请求参数
* @param encoding 编码
* @param token Token
* @param number Number
* @return 解密后的响应字符串
* @throws IOException
*/
public String send2(String url, JSONObject param, String encoding, String token, String number) throws IOException {
return send2(url, param, encoding, token, number, null);
}
/**
* 调用 Go 接口使用加密通信- 支持自定义 headers 和额外 headers
* @param url Go接口地址
* @param param 请求参数
* @param encoding 编码
* @param token Token
* @param number Number
* @param extraHeaders 额外的请求头
* @return 解密后的响应字符串
* @throws IOException
*/
public String send2(String url, JSONObject param, String encoding, String token, String number,
Map<String, String> extraHeaders) throws IOException {
// 1. 生成随机 Auth-key(16位)
String authKey = RandomStringUtil.generateRandomString(16);
// 2. 加密请求数据
String encryptedData;
try {
String jsonParam = JSON.toJSONString(param);
log.debug("原始请求参数: {}", jsonParam);
encryptedData = SM4Utils.encrypt(jsonParam, authKey);
log.debug("加密后的数据: {}", encryptedData);
} catch (Exception e) {
log.error("加密请求数据失败", e);
throw new IOException("加密请求数据失败: " + e.getMessage(), e);
}
// 3. 构建加密请求体
Map<String, String> encryptedBody = new HashMap<>();
encryptedBody.put("data", encryptedData); // Go后端期望data字段
String requestBody = JSON.toJSONString(encryptedBody);
// 4. 发送 HTTP 请求
CloseableHttpClient httpClient = null;
try {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.build();
httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build();
HttpPost httpPost = new HttpPost(url);
// 设置请求头 - 原有send2方法的headers
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Origin", "http://172.20.2.87");
httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
httpPost.setHeader("token", token);
httpPost.setHeader("number", number);
// 添加SM4加密相关的header
httpPost.setHeader("Auth-key", authKey);
// 添加额外的请求头
if (extraHeaders != null) {
extraHeaders.forEach(httpPost::setHeader);
}
// 设置请求体
StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
httpPost.setEntity(entity);
log.info("发送请求到: {}", url);
log.debug("请求头: token={}, number={}, Auth-key={}", token, number, authKey);
log.debug("请求体: {}", requestBody);
// 执行请求
HttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
log.debug("响应状态码: {}", statusCode);
if (statusCode != 200) {
String errorResponse = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
log.error("HTTP 请求失败,状态码: {}, 响应: {}", statusCode, errorResponse);
throw new IOException("HTTP 请求失败,状态码: " + statusCode + ", 响应: " + errorResponse);
}
// 获取响应体
String responseBody = EntityUtils.toString(response.getEntity(), encoding);
log.debug("原始响应体: {}", responseBody);
// 获取响应头中的 Auth-key
org.apache.http.Header[] authKeyHeaders = response.getHeaders("Auth-key");
String responseAuthKey = null;
if (authKeyHeaders != null && authKeyHeaders.length > 0) {
responseAuthKey = authKeyHeaders[0].getValue();
log.debug("响应头 Auth-key: {}", responseAuthKey);
}
// 5. 解密响应数据
if (responseAuthKey != null && !responseAuthKey.isEmpty()) {
return decryptGoResponse(responseBody, responseAuthKey);
} else {
log.warn("响应头中没有找到 Auth-key,返回原始响应");
return responseBody;
}
} catch (Exception e) {
log.error("HTTP 请求异常", e);
throw new IOException("HTTP 请求异常: " + e.getMessage(), e);
} finally {
if (httpClient != null) {
try {
httpClient.close();
} catch (IOException e) {
log.error("关闭 HTTP 客户端失败", e);
}
}
}
}
public String sendNoEncryptRequest(String url, Object param, String userKey, String userToken) throws IOException {
// 1. 生成随机 Auth-key(16位),用于解密响应
String authKey = RandomStringUtil.generateRandomString(16);
// 2. 不加密请求数据,直接发送JSON
String jsonParam = JSON.toJSONString(param);
String requestBody = jsonParam; // 这里不加密,直接使用原始JSON
// 3. 发送 HTTP 请求
CloseableHttpClient httpClient = null;
try {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.build();
httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build();
HttpPost httpPost = new HttpPost(url);
// 设置请求头
httpPost.setHeader("User-Key", userKey);
httpPost.setHeader("User-Token", userToken);
// 注意:这里我们不设置Auth-key,因为请求不加密
httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
// 设置请求体
StringEntity entity = new StringEntity(requestBody, StandardCharsets.UTF_8);
httpPost.setEntity(entity);
// 执行请求
HttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
String errorResponse = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
throw new IOException("HTTP 请求失败,状态码: " + statusCode + ", 响应: " + errorResponse);
}
// 获取响应体
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
// 获取响应头中的 Auth-key,用于解密响应
org.apache.http.Header[] authKeyHeaders = response.getHeaders("Auth-key");
String responseAuthKey = null;
if (authKeyHeaders != null && authKeyHeaders.length > 0) {
responseAuthKey = authKeyHeaders[0].getValue();
}
// 4. 解密响应数据
if (responseAuthKey != null && !responseAuthKey.isEmpty()) {
return decryptGoResponse(responseBody, responseAuthKey);
} else {
log.warn("响应头中没有找到 Auth-key,返回原始响应");
return responseBody;
}
} catch (Exception e) {
log.error("HTTP 请求异常", e);
throw new IOException("HTTP 请求异常: " + e.getMessage(), e);
} finally {
if (httpClient != null) {
try {
httpClient.close();
} catch (IOException e) {
log.error("关闭 HTTP 客户端失败", e);
}
}
}
}
}

69
src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoMd5EncryptionUtil.java

@ -0,0 +1,69 @@
package com.dreamchaser.depository_manage.utils.sm4;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class GoMd5EncryptionUtil {
private String appKey = "heng_xin_gao_ke_AppKey";
/**
* 与Go版本完全一致的MD5加密算法
* @param password 原始密码
* @return 加密后的字符串
*/
public String md5EncryptionAlgorithm(String password) {
try {
// Step1: 对AppKey进行MD5加密
String keyMd5 = md5(appKey);
// Step2: 对密码进行MD5加密
String codeMd1 = md5(password);
// Step3: 拼接codeMd1 + keyMd5
String yiCeng = codeMd1 + keyMd5;
// Step4: 对yiCeng进行MD5加密
String yiCengMd5 = md5(yiCeng);
// Step5: 拼接yiCengMd5 + AppKey
String erCeng = yiCengMd5 + appKey;
// Step6: 对erCeng进行MD5加密,得到最终结果
return md5(erCeng);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5加密算法不可用", e);
}
}
/**
* 标准的MD5加密方法32位小写
*/
private String md5(String input) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* 初始化方法如果需要
*/
public void md5EncryptionInit(String password) {
// 这里可以初始化密码,实际上Go版本中是设置Code字段
// 在Java中我们直接在方法参数中传递
}
}

47
src/main/java/com/dreamchaser/depository_manage/utils/sm4/RandomStringUtil.java

@ -0,0 +1,47 @@
package com.dreamchaser.depository_manage.utils.sm4;
import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Component;
import java.security.SecureRandom;
@Component
public class RandomStringUtil {
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* 生成指定长度的随机字符串
*/
public static String generateRandomString(int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int index = SECURE_RANDOM.nextInt(CHARACTERS.length());
sb.append(CHARACTERS.charAt(index));
}
return sb.toString();
}
/**
* 获取UUID
*/
public static String generateUUID() {
byte[] randomBytes = new byte[16];
SECURE_RANDOM.nextBytes(randomBytes);
randomBytes[6] &= 0x0f; /* clear version */
randomBytes[6] |= 0x40; /* set to version 4 */
randomBytes[8] &= 0x3f; /* clear variant */
randomBytes[8] |= 0x80; /* set to IETF variant */
char[] uuidChars = Hex.encodeHex(randomBytes, false);
StringBuilder uuid = new StringBuilder(36);
for (int i = 0; i < uuidChars.length; i++) {
if (i == 8 || i == 12 || i == 16 || i == 20) {
uuid.append('-');
}
uuid.append(uuidChars[i]);
}
return uuid.toString();
}
}

98
src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Interceptor.java

@ -0,0 +1,98 @@
package com.dreamchaser.depository_manage.utils.sm4;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.PrintWriter;
@Slf4j
@Component
public class SM4Interceptor implements HandlerInterceptor {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authKey = request.getHeader("Auth-key");
String contentType = request.getHeader("Content-Type");
log.debug("请求拦截 - Auth-key: {}, Content-Type: {}", authKey, contentType);
// 如果是JSON请求且需要解密
if (contentType != null && contentType.contains("application/json") && authKey != null) {
try {
// 读取请求体
StringBuilder requestBody = new StringBuilder();
try (BufferedReader reader = request.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
requestBody.append(line);
}
}
if (requestBody.length() > 0) {
String requestBodyStr = requestBody.toString();
//log.debug("原始请求体: {}", requestBodyStr);
// 解析加密请求
EncryptedRequest encryptedRequest = objectMapper.readValue(requestBodyStr, EncryptedRequest.class);
if (encryptedRequest.getData() != null && !encryptedRequest.getData().isEmpty()) {
//log.debug("获取到加密数据: {}", encryptedRequest.getData().substring(0, Math.min(50, encryptedRequest.getData().length())) + "...");
// 解密数据
String decryptedData = SM4Utils.decrypt(encryptedRequest.getData(), authKey);
//log.debug("解密后的数据: {}", decryptedData);
// 将解密后的数据重新放入请求体
request.setAttribute("decryptedBody", decryptedData);
// 记录解密成功
//log.info("请求解密成功,Auth-key: {}", authKey);
} else {
log.warn("加密数据为空,跳过解密");
}
}
} catch (Exception e) {
log.error("请求解密失败", e);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json;charset=UTF-8");
ApiResponse<Object> errorResponse = ApiResponse.error(100, "提交的数据格式错误!");
PrintWriter writer = response.getWriter();
writer.write(objectMapper.writeValueAsString(errorResponse));
writer.flush();
return false;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 生成新的随机密钥
String newAuthKey = RandomStringUtil.generateRandomString(16);
response.setHeader("Auth-key", newAuthKey);
request.setAttribute("responseAuthKey", newAuthKey);
//log.debug("生成新的 Auth-key: {}", newAuthKey);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
// 清理
request.removeAttribute("decryptedBody");
request.removeAttribute("responseAuthKey");
}
}

217
src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Utils.java

@ -0,0 +1,217 @@
package com.dreamchaser.depository_manage.utils.sm4;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
@Component
public class SM4Utils {
// Go 后端的 Sm4Key - 用作加密密钥
@Value("${sm4.key:hengxingaokeApp1}")
private String sm4Key;
// Go 后端的 Sm4Token - 默认用作 IV
@Value("${sm4.token:04TzMuvkHm_EZnHm}")
private String sm4Token;
// 前端使用的 systemKey(仅用于与前端对比验证)
@Value("${sm4.system-key:appKeyQinDongV01}")
private String systemKey;
private static String SM4_KEY_PROCESSED;
private static String SM4_TOKEN_PROCESSED;
private static String SYSTEM_KEY_HEX;
@PostConstruct
public void init() {
// Go 的方式:处理为16字节(不是转16进制!)
SM4_KEY_PROCESSED = processString(sm4Key, 16, "s");
SM4_TOKEN_PROCESSED = processString(sm4Token, 16, "s");
// 前端的 systemKey 转16进制(仅用于验证)
SYSTEM_KEY_HEX = stringToHex(systemKey);
/*System.out.println("=== SM4Utils 初始化 ===");
System.out.println("Go 的 Sm4Key: " + sm4Key);
System.out.println("Go 的 Sm4Key(处理后16字节): " + SM4_KEY_PROCESSED);
System.out.println("Go 的 Sm4Token: " + sm4Token);
System.out.println("Go 的 Sm4Token(处理后16字节): " + SM4_TOKEN_PROCESSED);
System.out.println("前端的 VITE_APP_SYSTEM_KEY: " + systemKey);
System.out.println("前端的 systemKey(hex): " + SYSTEM_KEY_HEX);
System.out.println("注意:Java 应该使用 Go 的 Sm4Key,而不是前端的 systemKey!");*/
}
/**
* 处理字符串为指定长度 Go ProcessString 完全一致
* 超过截断不足用 pad 补全
*/
private static String processString(String s, int length, String pad) {
// 如果字符串长度已经符合要求,直接返回
if (s.length() == length) {
return s;
}
// 如果字符串长度超过了要求,截取指定长度的部分
if (s.length() > length) {
return s.substring(0, length);
}
// 如果字符串长度不足,使用pad字符串进行补充
StringBuilder sb = new StringBuilder(s);
while (sb.length() < length) {
sb.append(pad);
}
return sb.substring(0, length); // 确保不会超过长度
}
/**
* 字符串转16进制仅用于与前端对比
*/
private static String stringToHex(String str) {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
StringBuilder hex = new StringBuilder();
for (byte b : bytes) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
/**
* 判断是否为32位16进制字符串
*/
private static boolean isValidHex32(String str) {
if (str == null || str.length() != 32) return false;
return str.matches("[0-9a-fA-F]{32}");
}
/**
* 加密方法 Go SM4Encrypt 完全一致
* @param data 要加密的字符串数据
* @param customKey 自定义密钥Auth-key如果提供则用作 IV
* @return 加密后的16进制字符串
*/
public static String encrypt(String data, String customKey) throws Exception {
// 1. 确定加密密钥(使用 Go 的 Sm4Key)
String keyStr = SM4_KEY_PROCESSED;
byte[] keyBytes = keyStr.getBytes(StandardCharsets.UTF_8);
// 2. 确定 IV(如果有 customKey 则用 customKey,否则用 Sm4Token)
String ivStr = SM4_TOKEN_PROCESSED;
if (customKey != null && !customKey.isEmpty()) {
// Go 的逻辑:如果有 customKey,处理为16字节
ivStr = processString(customKey, 16, "s");
}
byte[] ivBytes = ivStr.getBytes(StandardCharsets.UTF_8);
/*System.out.println("=== Java 加密参数 ===");
System.out.println("加密密钥: " + keyStr + " (UTF-8字节: " + bytesToHex(keyBytes) + ")");
System.out.println("IV: " + ivStr + " (UTF-8字节: " + bytesToHex(ivBytes) + ")");
System.out.println("原始数据: " + data);*/
// 3. 使用 BouncyCastle 进行 SM4-CBC 加密
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new SM4Engine()), new PKCS7Padding()
);
cipher.init(true, new ParametersWithIV(new KeyParameter(keyBytes), ivBytes));
byte[] inputBytes = data.getBytes(StandardCharsets.UTF_8);
byte[] outputBytes = new byte[cipher.getOutputSize(inputBytes.length)];
int length1 = cipher.processBytes(inputBytes, 0, inputBytes.length, outputBytes, 0);
int length2 = cipher.doFinal(outputBytes, length1);
byte[] encryptedBytes = new byte[length1 + length2];
System.arraycopy(outputBytes, 0, encryptedBytes, 0, encryptedBytes.length);
String result = bytesToHex(encryptedBytes);
//System.out.println("加密结果(hex): " + result);
return result;
}
/**
* 解密方法 Go SM4Decrypt 完全一致
* @param encryptedHex 加密后的16进制字符串
* @param customKey 自定义密钥Auth-key如果提供则用作 IV
* @return 解密后的字符串
*/
public static String decrypt(String encryptedHex, String customKey) throws Exception {
// 1. 确定加密密钥(使用 Go 的 Sm4Key)
String keyStr = SM4_KEY_PROCESSED;
byte[] keyBytes = keyStr.getBytes(StandardCharsets.UTF_8);
// 2. 确定 IV(如果有 customKey 则用 customKey,否则用 Sm4Token)
String ivStr = SM4_TOKEN_PROCESSED;
if (customKey != null && !customKey.isEmpty()) {
// Go 的逻辑:如果有 customKey,处理为16字节
ivStr = processString(customKey, 16, "s");
}
byte[] ivBytes = ivStr.getBytes(StandardCharsets.UTF_8);
/*System.out.println("=== Java 解密参数 ===");
System.out.println("解密密钥: " + keyStr + " (UTF-8字节: " + bytesToHex(keyBytes) + ")");
System.out.println("IV: " + ivStr + " (UTF-8字节: " + bytesToHex(ivBytes) + ")");
System.out.println("加密数据(hex): " + encryptedHex);*/
// 3. 转换加密数据
byte[] encryptedBytes = hexToBytes(encryptedHex);
// 4. 使用 BouncyCastle 进行 SM4-CBC 解密
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new SM4Engine()), new PKCS7Padding()
);
cipher.init(false, new ParametersWithIV(new KeyParameter(keyBytes), ivBytes));
byte[] outputBytes = new byte[cipher.getOutputSize(encryptedBytes.length)];
int length1 = cipher.processBytes(encryptedBytes, 0, encryptedBytes.length, outputBytes, 0);
int length2 = cipher.doFinal(outputBytes, length1);
byte[] decryptedBytes = new byte[length1 + length2];
System.arraycopy(outputBytes, 0, decryptedBytes, 0, decryptedBytes.length);
String result = new String(decryptedBytes, StandardCharsets.UTF_8);
//System.out.println("解密结果: " + result);
return result;
}
/**
* 字节数组转16进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hex = new StringBuilder();
for (byte b : bytes) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
/**
* 16进制字符串转字节数组
*/
private static byte[] hexToBytes(String hex) {
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException("Hex string length must be even");
}
byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
int index = i * 2;
bytes[i] = (byte) Integer.parseInt(hex.substring(index, index + 2), 16);
}
return bytes;
}
}

40
src/main/java/com/dreamchaser/depository_manage/utils/sm4/WebConfig.java

@ -0,0 +1,40 @@
package com.dreamchaser.depository_manage.utils.sm4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private SM4Interceptor sm4Interceptor;
@Autowired
private DecryptedArgumentResolver decryptedArgumentResolver;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(sm4Interceptor)
.addPathPatterns("/**")
.excludePathPatterns(
"/error",
"/api/sm4-test/**", // 排除测试接口
"/actuator/**", // 排除监控接口
"/swagger-ui/**", // 排除 Swagger
"/v2/api-docs", // 排除 API 文档
"/webjars/**" // 排除静态资源
);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(decryptedArgumentResolver);
}
}

13
src/main/resources/application-prod.yml

@ -96,3 +96,16 @@ mybatis-plus:
file:
uploadPath: D:/upLoad
# Go 后端的 Md5 配置(Java 应该使用这些)
app:
config:
appKey: heng_xin_gao_ke_AppKey
# Go 后端的 SM4 配置(Java 应该使用这些)
sm4:
key: "hengxingaokeApp1" # Go 的 Sm4Key
token: "04TzMuvkHm_EZnHm" # Go 的 Sm4Token
system-key: "appKeyQinDongV01"
app-key: "04TzMuvkHm_EZnHm"
Loading…
Cancel
Save