diff --git a/pom.xml b/pom.xml index 9b3f73c..b32ade5 100644 --- a/pom.xml +++ b/pom.xml @@ -266,6 +266,23 @@ spring-boot-starter-webflux + + com.alibaba.fastjson2 + fastjson2 + 2.0.46 + + + + org.bouncycastle + bcprov-jdk15to18 + 1.76 + + + commons-codec + commons-codec + 1.15 + + diff --git a/src/main/java/com/dreamchaser/depository_manage/config/PublicConfig.java b/src/main/java/com/dreamchaser/depository_manage/config/PublicConfig.java index 7f141fa..8142849 100644 --- a/src/main/java/com/dreamchaser/depository_manage/config/PublicConfig.java +++ b/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 getWaiKeyNumber(){ + /* + + * */ + String url = "http://172.20.2.87:39168/empower/gaintoken"; Map 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 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 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 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 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 TokenNumberMap = getWaiKeyNumber(); + post = goApiClient.send2(url, paramObject, HTTP.UTF_8, TokenNumberMap.get("token"), TokenNumberMap.get("number")); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/com/dreamchaser/depository_manage/controller/CompanyController.java b/src/main/java/com/dreamchaser/depository_manage/controller/CompanyController.java index 47c1e24..650d4a8 100644 --- a/src/main/java/com/dreamchaser/depository_manage/controller/CompanyController.java +++ b/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(); } diff --git a/src/main/java/com/dreamchaser/depository_manage/controller/IdentityVerifyController.java b/src/main/java/com/dreamchaser/depository_manage/controller/IdentityVerifyController.java index 925ca8c..f99025c 100644 --- a/src/main/java/com/dreamchaser/depository_manage/controller/IdentityVerifyController.java +++ b/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 map, HttpServletRequest request, HttpServletResponse response){ + public ModelAndView identityVerify(@RequestParam Map 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 paramForPost = new HashMap<>(); - paramForPost.put("code",algorithm); + Map 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"); @@ -112,4 +108,4 @@ public class IdentityVerifyController { return mv; } -} +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/controller/PageController.java b/src/main/java/com/dreamchaser/depository_manage/controller/PageController.java index b41744d..2e86d00 100644 --- a/src/main/java/com/dreamchaser/depository_manage/controller/PageController.java +++ b/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(); } diff --git a/src/main/java/com/dreamchaser/depository_manage/controller/UserController.java b/src/main/java/com/dreamchaser/depository_manage/controller/UserController.java index 9535f78..c598d7e 100644 --- a/src/main/java/com/dreamchaser/depository_manage/controller/UserController.java +++ b/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(); } diff --git a/src/main/java/com/dreamchaser/depository_manage/service/impl/CompanyServiceImpl.java b/src/main/java/com/dreamchaser/depository_manage/service/impl/CompanyServiceImpl.java index a53de8a..3807251 100644 --- a/src/main/java/com/dreamchaser/depository_manage/service/impl/CompanyServiceImpl.java +++ b/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 findAllUserByAdministration(String id,String userKey,String token){ - String url = PublicConfig.external_url +"/org/govthreeaboutman"; - Map map = new HashMap<>(); - map.put("id",id); - map.put("level",0); - map.put("all",1); + public List findAllUserByAdministration(String id, String userKey, String token) { + // 如果 GoApiClient 没有作为参数传入,需要注入 + GoApiClient goApiClient = new GoApiClient(); + + String url = PublicConfig.external_url + "/org/govthreeaboutman"; + Map 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 threeAboutManList = new ArrayList<>(); diff --git a/src/main/java/com/dreamchaser/depository_manage/service/impl/DepositoryRecordServiceImpl.java b/src/main/java/com/dreamchaser/depository_manage/service/impl/DepositoryRecordServiceImpl.java index e7c165c..ea5df37 100644 --- a/src/main/java/com/dreamchaser/depository_manage/service/impl/DepositoryRecordServiceImpl.java +++ b/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); } diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/ApiResponse.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/ApiResponse.java new file mode 100644 index 0000000..1bad976 --- /dev/null +++ b/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 { + 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 ApiResponse success(T data) { + if(null==data){ + return new ApiResponse(0, "成功", (T) "成功,没有相关数据"); + }else{ + return new ApiResponse<>(0, "成功", data); + } + + } + + public static ApiResponse error(int code, String msg) { + return new ApiResponse<>(code, msg, null); + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedArgumentResolver.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedArgumentResolver.java new file mode 100644 index 0000000..8951fdd --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedBody.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/DecryptedBody.java new file mode 100644 index 0000000..03b6843 --- /dev/null +++ b/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 { +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedRequest.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedRequest.java new file mode 100644 index 0000000..bc1d5ab --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedResponseAdvice.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/EncryptedResponseAdvice.java new file mode 100644 index 0000000..70586f6 --- /dev/null +++ b/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 { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + return true; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, + Class> 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; + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/ErrorCode.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/ErrorCode.java new file mode 100644 index 0000000..220cb62 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/GlobalExceptionHandler.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/GlobalExceptionHandler.java new file mode 100644 index 0000000..3a2f66c --- /dev/null +++ b/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 handleException(Exception e) { + log.error("系统异常:", e); + return ApiResponse.error(500, "服务器错误"); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ApiResponse handleIllegalArgumentException(IllegalArgumentException e) { + return ApiResponse.error(400, e.getMessage()); + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoApiClient.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoApiClient.java new file mode 100644 index 0000000..047260a --- /dev/null +++ b/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 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 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 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); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoMd5EncryptionUtil.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/GoMd5EncryptionUtil.java new file mode 100644 index 0000000..a655697 --- /dev/null +++ b/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中我们直接在方法参数中传递 + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/RandomStringUtil.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/RandomStringUtil.java new file mode 100644 index 0000000..6eb17cc --- /dev/null +++ b/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(); + } +} diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Interceptor.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Interceptor.java new file mode 100644 index 0000000..deefd84 --- /dev/null +++ b/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 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"); + } +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Utils.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/SM4Utils.java new file mode 100644 index 0000000..2d16188 --- /dev/null +++ b/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; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/dreamchaser/depository_manage/utils/sm4/WebConfig.java b/src/main/java/com/dreamchaser/depository_manage/utils/sm4/WebConfig.java new file mode 100644 index 0000000..f04bdb9 --- /dev/null +++ b/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 resolvers) { + resolvers.add(decryptedArgumentResolver); + } +} \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 2043852..c9a6f3b 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -95,4 +95,17 @@ mybatis-plus: mapper-locations: classpath*:*_mapper/*Mapper.xml file: - uploadPath: D:/upLoad \ No newline at end of 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" \ No newline at end of file