Browse Source

优化代码漏洞

dev-V1.0.5
vincent 2 months ago
parent
commit
7565fec9bd
  1. 8
      pom.xml
  2. 7
      zdxt-extend/zdxt-monitor-admin/Dockerfile
  3. 9
      zdxt-extend/zdxt-monitor-admin/src/main/resources/application.yml
  4. 9
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/IdCardCryptoUtil.java
  5. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ServiceInvocation.java
  6. 4
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/UserConstants.java
  7. 1
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEnterpriseLoginController.java
  8. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/utils/HttpUtil.java
  9. 36
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/helper/QrCodeHelper.java
  10. 4
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enforcement/impl/AppEnfLoginServiceImpl.java
  11. 15
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java
  12. 10
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/MssqApiServiceImpl.java
  13. 8
      zdxt-web-server/zdxt-admin-web-server/Dockerfile
  14. 8
      zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml
  15. 13
      zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-prod.yml
  16. 301
      zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/AppEnterpriseLoginControllerTest.java
  17. 329
      zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/cp/AppEnfCpPlanControllerTest.java
  18. 225
      zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/service/test/ResetUserPasswordTest.java

8
pom.xml

@ -74,21 +74,15 @@
<profile>
<id>local</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
<activation>
<!-- 默认环境 -->
@ -100,8 +94,6 @@
<properties>
<profiles.active>prod</profiles.active>
<logging.level>warn</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
</profiles>

7
zdxt-extend/zdxt-monitor-admin/Dockerfile

@ -7,6 +7,9 @@ LABEL maintainer="Lion Li"
RUN mkdir -p /ruoyi/monitor/logs
RUN groupadd -r appuser && useradd -r -g appuser -d /ruoyi/monitor -s /sbin/nologin appuser \
&& chown -R appuser:appuser /ruoyi/monitor
WORKDIR /ruoyi/monitor
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
@ -15,6 +18,10 @@ EXPOSE 9090
ADD ./target/zdxt-monitor-admin.jar ./app.jar
RUN chown appuser:appuser /ruoyi/monitor/app.jar
USER appuser
SHELL ["/bin/bash", "-c"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom \

9
zdxt-extend/zdxt-monitor-admin/src/main/resources/application.yml

@ -11,10 +11,6 @@ logging:
--- # 监控中心服务端配置
spring:
security:
user:
name: @monitor.username@
password: @monitor.password@
boot:
admin:
ui:
@ -44,8 +40,3 @@ spring.boot.admin.client:
url: http://localhost:9090/admin
instance:
service-host-type: IP
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@

9
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/IdCardCryptoUtil.java

@ -22,15 +22,6 @@ public class IdCardCryptoUtil {
* 以下字符串为密钥,可以通过配置来传递
* @deprecated 请使用 SiqConfig 配置类获取密钥
*/
@Deprecated
public static final String OUTER_AES_KEY = "u3BPiZWALItH27e34oO6bw==";
@Deprecated
public static final String INNER_AES_KEY = "ol1hV81iCruo+thxtZGnCg==";
@Deprecated
public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDADIVdRXNyT5pmGxSUfwEup2Y1YCz7VfibMyyQhkpVP3ByKsoFDjZ30yW5gv70sQbqn/MCthTFqwh3/I/YLfthM3vU7TdlmNfnlKcQ6prxYofLkX8akrhgQVFerb/4DhcbAk5xvx12Dkeh/nzDf5Qw2K3CymRPnr4zN6ZjAL79HwIDAQAB";
@Deprecated
public static final String PRIVATE_KEY = "MIICdAIBADANBgkqhkiG9w0BAQEFAASCAl4wggJaAgEAAoGBAMAMhV1Fc3JPmmYbFJR/AS6nZjVgLPtV+JszLJCGSlU/cHIqygUONnfTJbmC/vSxBuqf8wK2FMWrCHf8j9gt+2Eze9TtN2WY1+eUpxDqmvFih8uRfxqSuGBBUV6tv/gOFxsCTnG/HXYOR6H+fMN/lDDYrcLKZE+evjM3pmMAvv0fAgMBAAECf2rTWPVVrHuC/ywzFz+CmSXAxLNSZbMCH0doYvs0hIrmvtjCjgN1F6P1z2yUpHBEbsetZEwdyQnEf74+WCuBaFIYjmQm/05grIxam/QrGibnV5qNiA0VeCkj6bUy+z5mt3/ithgzjmOiYIgMenjf6ut6RmgRDodgJiFbvSvfskECQQDoqGMNaUV+Y/O9h+l9eOCkSUOjOsL9WDRL34ei/jf7VFFYBZLs2GMzLunDkDz1dHvufDiMPeHUTTN8zIEEpNMVAkEA01Ehf9Np2zUqFQp3SW/fHVsaGM97JsbsTQrsDdUWwZLJTSQ7Yf+yg21GkNa8Mmlsp1OLyS607HV7lPlKO0LsYwJBAN1LTOv0taUCbVRZcj1mPEYHac5Ylh9jRlrkwp7GL3lfOf//TUD6kWCdvrvE9jaCFV9ByIecVLEavV53JkDEhgECQHgR40z0XXOWs8Cc38a0cDwH5b4vNjSEVwQ3roT1jSaUNxlD+xHl7hLLZosp2Gl0ia5RxF2d0mOBJaUyOMGPdy0CQCLBU3995JMoUyLgt2+Riw0PFbdxFEnKGyshAt5Q957/hC6L/V5tQNnh/AM9q23DIUQGb1whhxdIzV12qFSSS34=";
public static void main(String[] args) {
String key = "VRJHzFqJpsSZhwnsKTWNspv_GZMxTWl-bgH6DyE9Ty0";
System.out.println("====================== 示例 ======================");

2
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ServiceInvocation.java

@ -261,7 +261,7 @@ public class ServiceInvocation {
paramList.add(pair);
}
SSLContext sc = SSLContext.getInstance("SSL");
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, new TrustManager[] { new HttpUtil.TrustAnyTrustManager() }, new SecureRandom());
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)

4
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/UserConstants.java

@ -111,10 +111,6 @@ public class UserConstants
*/
public static final Long DEFAULT_ROLE = 178L;
/**
* 默认密码
*/
public static final String DEFAULT_PASSWORD = "szsf@zfjd";
/**
* 默认部门

1
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEnterpriseLoginController.java

@ -83,7 +83,6 @@ public class AppEnterpriseLoginController {
log.info("---> 深i企登录用户 code:{}",siqLogin.getCode());
String secret = IdCardCryptoUtil.symmetricEncryptIdCard(secretKey, siqLogin.getCode());
params.put("accessKeySign",secret);
log.info("accessKeySign{}",secret);
params.put("appCode",code);
String data = null;
try {

2
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/utils/HttpUtil.java

@ -26,7 +26,7 @@ public class HttpUtil {
public static String HttpSendGet(String url,String accessToken) throws Exception{
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response=null;
SSLContext sc = SSLContext.getInstance("SSL");
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)

36
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/helper/QrCodeHelper.java

@ -86,15 +86,12 @@ public class QrCodeHelper {
// 2. 拼接明文并生成 vaCode(AES加密)
String vaCode = symmetricRQValidateCodeEncrypt(enterpriseNumber);
Map<String, String> jsonMap = new HashMap<>();
jsonMap.put("salt", salt);
jsonMap.put("codeId", codeId);
jsonMap.put("type", "zfjd");
jsonMap.put("enterpriseId", enterpriseId);
jsonMap.put("enterpriseNumber", enterpriseNumber);
jsonMap.put("vaCode", vaCode);
jsonMap.put("formModule","EntPubQrcodeService");
jsonMap.put("createDate", DateUtils.getTime());
jsonMap.put("refExId", refExId);
@ -213,37 +210,4 @@ public class QrCodeHelper {
}
}
// ==================== 加密与辅助方法 ====================
/**
* AES/ECB/PKCS5Padding 对称加密
*/
private static String symmetricRQValidateCodeEncrypt(String content) {
try {
javax.crypto.spec.SecretKeySpec secretKey = new javax.crypto.spec.SecretKeySpec(AES_KEY.getBytes(), "AES");
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKey);
byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
return java.util.Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("AES加密失败", e);
}
}
/**
* 构建二维码明文内容
*/
private static String buildQrCodeContent(String salt, String codeId, String type,
String enterpriseId, String enterpriseNumber,
String formModule, Date createDate, String eprqId) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = (createDate != null) ? sdf.format(createDate) : "";
return String.join("|", salt, codeId,
nullToEmpty(type), nullToEmpty(enterpriseId), nullToEmpty(enterpriseNumber),
nullToEmpty(formModule), dateStr, nullToEmpty(eprqId));
}
private static String nullToEmpty(String s) {
return s != null ? s : "";
}
}

4
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enforcement/impl/AppEnfLoginServiceImpl.java

@ -293,13 +293,9 @@ public class AppEnfLoginServiceImpl implements IAppEnfLoginService {
// 证件号解密
String secretKey = srnwgConfig.getSecretKey();
String card = IdCardCryptoUtil.symmetricDecryptIdCard(secretKey, userIdCard);
log.debug("身份证解密用盐: {}", secretKey);
log.debug("解密后的身份证号: {}", card);
// 查询执法人员信息
List<Zfryjdm2405301> zfryList = zfryjdm2405301Mapper.queryZFRYByIdCard(card);
log.debug("查询到的执法人员信息: {}", zfryList);
// 判断是否有执法证编号
if (zfryList != null && !zfryList.isEmpty()) {
Zfryjdm2405301 zfry = zfryList.get(0);

15
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/enterprise/impl/AppEnterpriseLoginServiceImpl.java

@ -4,6 +4,7 @@ package com.zdxt.enforcementcode.service.enterprise.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
@ -118,7 +119,7 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService
@Override
public Map<String, Object> siqLogin(AppEnterpriseLoginBo bo) {
JSONObject userInfo = siqLoginInfo(bo);
log.info("---> 深i企登录用户信息:{}", userInfo);
// log.info("---> 深i企登录用户信息:{}", userInfo);
return doSiqLoginWithLock(userInfo);
}
@ -134,22 +135,15 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService
try {
locked = lock.tryLock(5, 60*5, TimeUnit.SECONDS);
if (!locked) {
log.info("用户正在登录中,用户信息:{}", userInfo.toJSONString());
throw new BaseException("用户正在登录中,请稍后再试");
}
log.info("---> 登入锁 key: {}", lockKey);
EnterpriseUser newUser = saveOrUpdateEnterpriseUser(userInfo);
log.info("---> newUser: {}", newUser);
EnterpriseInformation enterpriseInformation = addSiqUserCompany(userInfo, newUser);
log.info("---> enterpriseInformation: {}", enterpriseInformation);
if (newUser != null && enterpriseInformation != null) {
createUserEnterpriseRef(newUser.getUserId(), enterpriseInformation);
}
String token = generateToken(newUser, enterpriseInformation);
log.info("---> token: {}", token);
return buildLoginResult(newUser, enterpriseInformation, token);
} catch (BaseException e) {
throw e;
@ -220,7 +214,6 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService
EnterpriseUser existUser = enterpriseUserMapper.selectOne(queryWrapper);
Long userId;
log.info("===> existUser: {}", existUser);
if (existUser == null) {
log.info("企业用户不存在,执行新增操作: userName={}, userType={}", loginName, userType);
newUser = buildEnterpriseUser(userInfo, userType);
@ -259,7 +252,7 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService
user.setDeptId(UserConstants.DEFAULT_DEPT);
// 设置默认密码(MD5加盐加密)
String defaultPassword = UserConstants.DEFAULT_PASSWORD;
String defaultPassword = RandomUtil.randomNumbers(8);
user.setPassword(DigestUtil.md5Hex(user.getLoginName() + defaultPassword + user.getSalt()));
// 账号状态:正常
@ -553,7 +546,7 @@ public class AppEnterpriseLoginServiceImpl implements IAppEnterpriseLoginService
// 调试:检查 Token 是否成功存储到 Redis
long timeout = StpUtil.getTokenTimeout();
log.info("生成token成功: userId={}, token={}, timeout={}秒, platformType={}", user.getUserId(), token, timeout, loginUser.getPlatformType());
log.info("Token session key: login:token:{}", token);
// log.info("Token session key: login:token:{}", token);
return token;
}

10
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/MssqApiServiceImpl.java

@ -52,7 +52,7 @@ public class MssqApiServiceImpl implements IMssqApiService {
//请求类型
mssqAddOrder.setInvokingServiceRequestMethod("POST");
String s = JSONObject.toJSONString(mssqAddOrder);
log.info("民生诉求token:{}",JSONObject.toJSONString(mssqToken));
// log.info("民生诉求token:{}",JSONObject.toJSONString(mssqToken));
log.info("民生诉求工单新增,上传参数:{}",s);
JSONObject serviceJsonData = ServiceInvocation.httpRequestForInterface(mssqUrl + "/api/API_YX/handlerProcess/channelAddOrder",
mssqAddOrder.getInvokingServiceRequestMethod(), s, appId, mssqToken.getGatewayRtime(), mssqToken.getGatewaySig());
@ -79,14 +79,11 @@ public class MssqApiServiceImpl implements IMssqApiService {
// log.info("民生诉求获取token接口:{}",jsonObject);
if (jsonObjectBody != null) {
String accessToken = jsonObjectBody.getString("access_token");
log.info("民生诉求获取token:{}",accessToken);
// 3.在获得服务调用秘钥access_token后,根据自己的appKey使用AES解密算法对返回值进行解密,最终获取签名密钥secretKey
String secretKey = ServiceInvocation.gatewayAesDecode(appKey, accessToken);
log.info("民生诉求获取secretKey:{}",secretKey);
// 4.获取本地签名
String sign = gatewaySignEncode(appId, secretKey, currTime);
mssqBaseDomain.setAccessToken(accessToken);
log.info("民生诉求获取sign:{}",sign);
//签名
mssqBaseDomain.setGatewaySig(sign);
}
@ -206,19 +203,14 @@ public class MssqApiServiceImpl implements IMssqApiService {
String tokenSign = gatewaySignEncode(appId, appKey, currTime);
// 2.调用获取token接口
JSONObject jsonObject = httpRequest(mssqUrl + "/auth/token", "POST", "", hcpAppId, hcpTimestamp, hcpSign);
log.info("民生诉求获取auth:{}",jsonObject.toJSONString());
JSONObject jsonObjectBody = JSONObject.parseObject(jsonObject.getString("body"));
// log.info("民生诉求获取token接口:{}",jsonObject);
if (jsonObjectBody != null) {
String accessToken = jsonObjectBody.getString("access_token");
log.info("民生诉求获取token:{}",accessToken);
// 3.在获得服务调用秘钥access_token后,根据自己的appKey使用AES解密算法对返回值进行解密,最终获取签名密钥secretKey
String secretKey = ServiceInvocation.gatewayAesDecode(appKey, accessToken);
log.info("民生诉求获取secretKey:{}",secretKey);
// 4.获取本地签名
String sign = gatewaySignEncode(appId, secretKey, currTime);
mssqBaseDomain.setAccessToken(accessToken);
log.info("民生诉求获取sign:{}",sign);
//签名
mssqBaseDomain.setGatewaySig(sign);
}

8
zdxt-web-server/zdxt-admin-web-server/Dockerfile

@ -9,6 +9,10 @@ RUN mkdir -p /ruoyi/server/logs \
/ruoyi/server/temp \
/ruoyi/skywalking/agent
RUN groupadd -r appuser && useradd -r -g appuser -d /ruoyi/server -s /sbin/nologin appuser \
&& chown -R appuser:appuser /ruoyi/server \
&& chown -R appuser:appuser /ruoyi/skywalking
WORKDIR /ruoyi/server
ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
@ -19,6 +23,10 @@ EXPOSE ${SNAIL_PORT}
ADD ./target/zdxt-admin.jar ./app.jar
RUN chown appuser:appuser /ruoyi/server/app.jar
USER appuser
SHELL ["/bin/bash", "-c"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \

8
zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml

@ -284,10 +284,6 @@ justauth:
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea
#security:
# excludes:
# - /**
#深i企对接密钥 测试环境
siq:
#正式
@ -340,6 +336,10 @@ srnwg:
paasToken: ENC(VfUgR5OXHJcKvBBfq/2ysuNPH6I7tCCeWHYSzXKicGChlMiw9mVPGD51naX9w42E)
#市认证数据加密字符
secretKey: ENC(xWBA5VwT23j14A8+6I15HCerJvo3zaRxzVE2xc0i7wZ7wq/SCflOEkHCc8wwdi0KHkDGjKIoHhs=)
# RSA 公钥
rsa-public-key: ENC(待用Jasypt加密后的公钥密文)
# RSA 私钥
rsa-private-key: ENC(待用Jasypt加密后的私钥密文)
# #测试
# url: ENC(jFwf6bYIMn+FOtbCkyPr0r+TBOPrp84tNotl912ilUZy2e9Y8cbH3uDQ5jtr6SAa)
# systemCode: ENC(ECOnqNVVPmCmnqhde6tsS5JEQYPtlmCv)

13
zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-prod.yml

@ -7,19 +7,6 @@ zdxt:
url-prefix: /profile
demo_enabled: false
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: false
url: http://localhost:9090/admin
instance:
service-host-type: IP
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@
--- # snail-job 配置
snail-job:
enabled: false

301
zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/AppEnterpriseLoginControllerTest.java

@ -1,301 +0,0 @@
//package com.zdxt.rest.test;
//
//import cn.dev33.satoken.context.mock.SaTokenContextMockUtil;
//import org.dromara.ZdxtAdminApplication;
//import org.junit.jupiter.api.*;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
//import org.springframework.boot.test.context.SpringBootTest;
//import org.springframework.http.MediaType;
//import org.springframework.test.web.servlet.MockMvc;
//import org.springframework.test.web.servlet.MvcResult;
//
//import java.nio.charset.StandardCharsets;
//
//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
//import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
//
///**
// * AppEnterpriseLoginController 企业端-用户登录接口测试
// * <p>
// * 覆盖以下接口:
// * 1. POST /app-ent/login/siqLogin - 深i企用户登录
// * </p>
// * <p>
// * 注意:此测试需要配置真实的深i企接口地址,或在测试环境中 Mock 相关服务
// * </p>
// *
// * @author zdxt
// */
//@SpringBootTest(classes = ZdxtAdminApplication.class)
//@AutoConfigureMockMvc
//@DisplayName("AppEnterpriseLoginController 企业端-用户登录接口测试")
//@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
//public class AppEnterpriseLoginControllerTest {
//
// @Autowired
// private MockMvc mockMvc;
//
// private static final String BASE_URL = "/app-ent/login";
// private static final String TEST_CLIENT_ID = "e5cd7e4891bf95d1d19206ce24a7b32e";
//
// @BeforeEach
// public void setUp() {
// SaTokenContextMockUtil.setMockContext();
// }
//
// @AfterEach
// public void tearDown() {
// SaTokenContextMockUtil.clearContext();
// }
//
// // ==================== siqLogin 接口测试 ====================
//
// @DisplayName("深i企登录 - 正常请求(需要有效的授权码)")
// @Test
// @Order(1)
// public void testSiqLoginSuccess() throws Exception {
// String requestBody = "{\"code\":\"test_authorization_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
// System.out.println("深i企登录接口响应: " + responseBody);
//
// if (responseBody.contains("\"code\":200")) {
// Assertions.assertTrue(responseBody.contains("token"), "返回数据应包含 token");
// Assertions.assertTrue(responseBody.contains("userId"), "返回数据应包含 userId");
// Assertions.assertTrue(responseBody.contains("userName"), "返回数据应包含 userName");
// Assertions.assertTrue(responseBody.contains("enterpriseId"), "返回数据应包含 enterpriseId");
// Assertions.assertTrue(responseBody.contains("enterpriseName"), "返回数据应包含 enterpriseName");
// System.out.println("深i企登录成功,返回了完整的用户和企业信息");
// } else {
// System.out.println("深i企登录失败(可能由于授权码无效或深i企接口未配置): " + responseBody);
// }
// }
//
// @DisplayName("深i企登录 - 缺少授权码参数应返回失败")
// @Test
// @Order(2)
// public void testSiqLoginMissingCode() throws Exception {
// String requestBody = "{}";
//
// mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andExpect(jsonPath("$.code").value(500));
//
// System.out.println("缺少授权码参数返回失败测试成功");
// }
//
// @DisplayName("深i企登录 - 空授权码应返回失败")
// @Test
// @Order(3)
// public void testSiqLoginEmptyCode() throws Exception {
// String requestBody = "{\"code\":\"\"}";
//
// mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andExpect(jsonPath("$.code").value(500));
//
// System.out.println("空授权码返回失败测试成功");
// }
//
// @DisplayName("深i企登录 - 无效授权码应返回失败")
// @Test
// @Order(4)
// public void testSiqLoginInvalidCode() throws Exception {
// String requestBody = "{\"code\":\"invalid_code_12345\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
//
// if (responseBody.contains("\"code\":500")) {
// System.out.println("无效授权码返回失败测试成功");
// } else {
// System.out.println("注意:无效授权码未返回预期错误,响应: " + responseBody);
// }
// }
//
// @DisplayName("深i企登录 - 验证返回数据结构完整性")
// @Test
// @Order(5)
// public void testSiqLoginResponseStructure() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
// System.out.println("验证返回数据结构响应: " + responseBody);
//
// if (responseBody.contains("\"code\":200")) {
// Assertions.assertTrue(responseBody.contains("\"data\""), "返回数据应包含 data 字段");
//
// String data = extractDataJson(responseBody);
// if (data != null) {
// Assertions.assertTrue(data.contains("token"), "data 中应包含 token");
// Assertions.assertTrue(data.contains("userId"), "data 中应包含 userId");
// Assertions.assertTrue(data.contains("userName"), "data 中应包含 userName");
// Assertions.assertTrue(data.contains("loginName"), "data 中应包含 loginName");
// Assertions.assertTrue(data.contains("phonenumber"), "data 中应包含 phonenumber");
// Assertions.assertTrue(data.contains("enterpriseId"), "data 中应包含 enterpriseId");
// Assertions.assertTrue(data.contains("enterpriseName"), "data 中应包含 enterpriseName");
// Assertions.assertTrue(data.contains("enterpriseNumber"), "data 中应包含 enterpriseNumber");
// System.out.println("返回数据结构完整,包含所有必需字段");
// }
// } else {
// System.out.println("登录失败,无法验证数据结构");
// }
// }
//
// @DisplayName("深i企登录 - 重复登录应能正常处理(同一授权码多次调用)")
// @Test
// @Order(6)
// public void testSiqLoginDuplicateCall() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// for (int i = 1; i <= 3; i++) {
// System.out.println("========== 第 " + i + " 次调用深i企登录 ==========");
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
// System.out.println("第 " + i + " 次调用响应: " + responseBody);
//
// Thread.sleep(1000);
// }
//
// System.out.println("========== 重复登录测试完成 ==========");
// }
//
// @DisplayName("深i企登录 - 验证 Token 格式")
// @Test
// @Order(7)
// public void testSiqLoginTokenFormat() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
//
// if (responseBody.contains("\"code\":200")) {
// String token = extractTokenFromResponse(responseBody);
// if (token != null && !token.isEmpty()) {
// Assertions.assertTrue(token.length() > 10, "Token 长度应大于10个字符");
// System.out.println("Token 格式验证通过,Token: " + token);
// } else {
// System.out.println("未能提取到 Token");
// }
// } else {
// System.out.println("登录失败,无法验证 Token 格式");
// }
// }
//
// @DisplayName("深i企登录 - 验证企业信息关联")
// @Test
// @Order(8)
// public void testSiqLoginEnterpriseAssociation() throws Exception {
// String requestBody = "{\"code\":\"test_code\"}";
//
// MvcResult result = mockMvc.perform(post(BASE_URL + "/siqLogin")
// .contentType(MediaType.APPLICATION_JSON)
// .content(requestBody))
// .andDo(print())
// .andExpect(status().isOk())
// .andReturn();
//
// String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
//
// if (responseBody.contains("\"code\":200")) {
// String data = extractDataJson(responseBody);
// if (data != null) {
// String enterpriseId = extractJsonValue(data, "enterpriseId");
// String enterpriseName = extractJsonValue(data, "enterpriseName");
// String enterpriseNumber = extractJsonValue(data, "enterpriseNumber");
//
// if (enterpriseId != null && enterpriseName != null && enterpriseNumber != null) {
// Assertions.assertFalse(enterpriseId.isEmpty(), "企业ID不应为空");
// Assertions.assertFalse(enterpriseName.isEmpty(), "企业名称不应为空");
// Assertions.assertFalse(enterpriseNumber.isEmpty(), "企业统一信用代码不应为空");
// System.out.println("企业信息关联验证通过");
// System.out.println("企业ID: " + enterpriseId);
// System.out.println("企业名称: " + enterpriseName);
// System.out.println("企业统一信用代码: " + enterpriseNumber);
// }
// }
// } else {
// System.out.println("登录失败,无法验证企业信息关联");
// }
// }
//
// // ==================== 工具方法 ====================
//
// /**
// * 从 JSON 响应中提取 data 字段
// */
// private String extractDataJson(String responseBody) {
// if (responseBody != null && responseBody.contains("\"data\":{")) {
// int start = responseBody.indexOf("\"data\":{") + 7;
// int end = responseBody.lastIndexOf("}");
// if (start > 0 && end > start) {
// return responseBody.substring(start, end + 1);
// }
// }
// return null;
// }
//
// /**
// * 从响应中提取 Token
// */
// private String extractTokenFromResponse(String responseBody) {
// return extractJsonValue(responseBody, "token");
// }
//
// /**
// * 从 JSON 字符串中提取指定字段的值
// */
// private String extractJsonValue(String json, String key) {
// if (json != null && json.contains("\"" + key + "\"")) {
// int start = json.indexOf("\"" + key + "\":\"") + key.length() + 4;
// int end = json.indexOf("\"", start);
// if (start > 0 && end > start) {
// return json.substring(start, end);
// }
// }
// return null;
// }
//}
//{
//}

329
zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/cp/AppEnfCpPlanControllerTest.java

@ -1,329 +0,0 @@
package com.zdxt.rest.test.cp;
import cn.dev33.satoken.context.mock.SaTokenContextMockUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zdxt.enforcementcode.domain.bo.EnterpriseInformationBo;
import com.zdxt.enforcementcode.domain.vo.EnterpriseInformationVo;
import com.zdxt.enforcementcode.service.IEnterpriseInformationService;
import org.dromara.ZdxtAdminApplication;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.satoken.utils.LoginHelper;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* AppEnfCpPlanController 新增计划批量测试
* <p>
* 为多个用户批量创建执法计划(每人100条)
* </p>
*/
@SpringBootTest(classes = ZdxtAdminApplication.class)
@AutoConfigureMockMvc
@ActiveProfiles({"dev", "dev-cust-zrb"})
@DisplayName("AppEnfCpPlanController 批量新增计划测试")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class AppEnfCpPlanControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private IEnterpriseInformationService enterpriseInformationService;
private static final String BASE_URL = "/app-enf/cpPlan";
private static final String LOGIN_URL = "/app-enf/login/do";
private static final String AES_KEY = "1234567898745621";
private static final String CLIENT_ID = "f0535c2a32ab4d0496ab9a699b2a7d36";
private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
/** 通用密码 */
private static final String PASSWORD = "szsf@zfjd";
@BeforeEach
public void setUp() {
// 初始化 Sa-Token 上下文
SaTokenContextMockUtil.setMockContext();
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockRequest));
}
@AfterEach
public void tearDown() {
try {
StpUtil.logout();
} catch (Exception ignored) {
}
RequestContextHolder.resetRequestAttributes();
SaTokenContextMockUtil.clearContext();
}
/**
* 登录指定账号,返回登录信息
*/
private Map<String, String> doLogin(String account) throws Exception {
String encryptedPassword = encryptByAes(PASSWORD);
Map<String, String> loginParams = new HashMap<>();
loginParams.put("secretName", account);
loginParams.put("secretKey", encryptedPassword);
MvcResult loginResult = mockMvc.perform(post(LOGIN_URL)
.header("User-Agent", USER_AGENT)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginParams)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andReturn();
String loginBody = loginResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
JsonNode loginJson = objectMapper.readTree(loginBody);
JsonNode data = loginJson.get("data");
Map<String, String> info = new HashMap<>();
info.put("token", data.get("token").asText());
info.put("userId", data.get("userId").asText());
// nickName 可能为空,回退到 userName(中文姓名)
String nickName = data.has("nickName") ? data.get("nickName").asText() : "";
if (nickName == null || nickName.isBlank()) {
nickName = data.has("userName") ? data.get("userName").asText() : account;
}
info.put("nickName", nickName);
info.put("deptId", data.get("deptId").asText());
info.put("deptName", data.get("deptName").asText());
info.put("organId", data.get("organId").asText());
info.put("organName", data.get("organName").asText());
info.put("bureauUnitId", data.get("bureauUnitId").asText());
info.put("bureauUnitName", data.get("bureauUnitName").asText());
info.put("phonenumber", data.has("phonenumber") ? data.get("phonenumber").asText() : "");
System.out.println("[" + account + "] 登录成功: userId=" + info.get("userId"));
return info;
}
// ==================== 各账号批量新增测试 ====================
@DisplayName("liuqianlin(刘倩琳)批量新增100条执法计划")
@Test
@Order(1)
public void testBatchCreate_liuqianlin() throws Exception {
batchCreatePlans("liuqianlin", 100);
}
@DisplayName("baining(白宁)批量新增100条执法计划")
@Test
@Order(2)
public void testBatchCreate_baining() throws Exception {
batchCreatePlans("baining", 100);
}
@DisplayName("caizifan(蔡梓帆)批量新增100条执法计划")
@Test
@Order(3)
public void testBatchCreate_caizifan() throws Exception {
batchCreatePlans("caizifan", 100);
}
@DisplayName("chenchong4(陈冲)批量新增100条执法计划")
@Test
@Order(4)
public void testBatchCreate_chenchong4() throws Exception {
batchCreatePlans("chenchong4", 100);
}
/**
* 为指定账号批量新增计划
* <p>
* 唯一性约束:同一人 + 日期 + 时间段 + 企业 只能有一条计划。
* 日期循环31天(7月1~31日),时间段2个(上午/下午),企业循环7家,
* 因 31、2、7 两两互素,由中国剩余定理(CRT)保证前 31×2×7=434 条组合均唯一,
* 故 count≤434 时所有计划不会重复。
* </p>
*/
private void batchCreatePlans(String account, int count) throws Exception {
Map<String, String> loginInfo = doLogin(account);
String token = loginInfo.get("token");
String userId = loginInfo.get("userId");
String nickName = loginInfo.get("nickName");
// 从数据库加载指定企业列表
List<EnterpriseInformationVo> enterprises = loadEnterprises();
Assertions.assertFalse(enterprises.isEmpty(), "数据库中无可用企业数据");
// --- 唯一性预测验证 ---
int days = 31; // 7月有31天
int timeSlots = 2; // 上午/下午
int entCount = enterprises.size();
// 由CRT保证的最大不重复组合数 = lcm(days, timeSlots, entCount) 当两两互素时 = days * timeSlots * entCount
int maxUnique = days * timeSlots * entCount;
int expectedSuccess = Math.min(count, maxUnique);
System.out.println("[" + account + "] 企业数=" + entCount
+ ", 可用唯一组合(日期×时段×企业)=" + maxUnique
+ ", 计划生成数=" + count
+ ", 预计成功数=" + expectedSuccess);
Assertions.assertTrue(count <= maxUnique,
"计划数(" + count + ")超过最大唯一组合数(" + maxUnique + "),必然产生重复");
Random random = new Random();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
int successCount = 0;
int failCount = 0;
for (int i = 0; i < count; i++) {
// 所有计划日期限定在2026年7月内,循环使用7月1日~31日
int dayOfMonth = (i % days) + 1;
cal.set(2026, Calendar.JULY, dayOfMonth);
String planDate = sdf.format(cal.getTime());
long planTime = (i % 2 == 0) ? 1L : 2L; // 交替上午/下午
// 确定性循环选取企业,保证 (日期, 时段, 企业) 三元组唯一
EnterpriseInformationVo enterprise = enterprises.get(i % entCount);
Map<String, Object> planBo = buildPlanBo(planDate, planTime, i, nickName, userId, loginInfo, enterprise, random);
String requestBody = objectMapper.writeValueAsString(planBo);
MvcResult result = mockMvc.perform(post(BASE_URL)
.header("Authorization", "Bearer " + token)
.header("clientid", CLIENT_ID)
.header("User-Agent", USER_AGENT)
.contentType(MediaType.APPLICATION_JSON)
.content(requestBody))
.andReturn();
String responseBody = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
JsonNode respJson = objectMapper.readTree(responseBody);
int code = respJson.get("code").asInt();
if (code == 200) {
successCount++;
} else {
failCount++;
System.out.println("[" + account + "] 第" + (i + 1) + "条失败: " + responseBody);
}
if ((i + 1) % 10 == 0) {
System.out.println("[" + account + "] 已处理 " + (i + 1) + "/" + count
+ ", 成功=" + successCount + ", 失败=" + failCount);
}
}
System.out.println("========== [" + account + "] 批量新增完成 ==========");
System.out.println("总计: " + count + ", 成功: " + successCount + ", 失败: " + failCount);
Assertions.assertEquals(expectedSuccess, successCount,
"[" + account + "] 预期成功" + expectedSuccess + "条,实际成功" + successCount + "条");
}
/** 指定的企业统一社会信用代码列表 */
private static final String[][] ENTERPRISE_DATA = {
{"吉田拉链(深圳)有限公司公明工厂", "91440300788302281W"},
{"欧姆龙电子部件(深圳)有限公司", "914403007261811621"},
{"深圳农牧美益肉业有限公司", "91440300662678582M"},
{"深圳市深水龙岗污水处理有限公司", "914403006820066603"},
{"深圳市强达电路股份有限公司", "91440300763461366N"},
{"深圳康泰生物制品股份有限公司", "91440300618837873J"},
{"深圳市里程食品有限公司", "91440300738848091F"},
};
/**
* 从数据库加载指定的7家企业
*/
private List<EnterpriseInformationVo> loadEnterprises() {
List<EnterpriseInformationVo> result = new ArrayList<>();
for (String[] entry : ENTERPRISE_DATA) {
EnterpriseInformationBo queryBo = new EnterpriseInformationBo();
queryBo.setEnterpriseNumber(entry[1]);
queryBo.setIsDelete(0L);
List<EnterpriseInformationVo> list = enterpriseInformationService.queryList(queryBo);
if (!list.isEmpty()) {
result.add(list.get(0));
} else {
System.out.println("[警告] 未找到企业: " + entry[0] + " (" + entry[1] + ")");
}
}
return result;
}
/**
* 构建计划BO的Map
*/
private Map<String, Object> buildPlanBo(String planDate, long planTime, int index,
String userName, String userId, Map<String, String> loginInfo,
EnterpriseInformationVo enterprise, Random random) {
Map<String, Object> bo = new LinkedHashMap<>();
bo.put("planType", 1L); // 日常计划
bo.put("planDate", planDate);
bo.put("planTime", planTime);
bo.put("enforceContent", "批量测试执法事项-第" + (index + 1) + "条");
bo.put("enterpriseName", enterprise.getEnterpriseName());
bo.put("enterpriseNumber", enterprise.getEnterpriseNumber());
bo.put("enterpriseId", enterprise.getId());
bo.put("enforceUserNames", userName);
bo.put("enforceUserIds", userId);
bo.put("enforceUserTels", loginInfo.get("phonenumber"));
bo.put("enforceUserCodes", "-");
bo.put("isPublic", random.nextInt(2) == 0 ? 0L : 1L); // 随机公开/不公开
bo.put("isUnion", 1L); // 必须为联合执法,否则afterInsert会直接生成任务导致status变为3
bo.put("reserveStatus", 1L); // 待预约
bo.put("systemReserved", 0L);
bo.put("manualReserved", 0L);
bo.put("bureauDeptId", loginInfo.get("bureauUnitId"));
bo.put("bureauDeptName", loginInfo.get("bureauUnitName"));
bo.put("organId", loginInfo.get("organId"));
bo.put("organName", loginInfo.get("organName"));
bo.put("deptId", loginInfo.get("deptId"));
bo.put("deptName", loginInfo.get("deptName"));
bo.put("isDelete", 0L);
return bo;
}
/**
* AES/CBC/ZeroPadding 加密(与前端 CryptoJS 对应)
*/
private String encryptByAes(String plainText) throws Exception {
byte[] keyBytes = AES_KEY.getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = AES_KEY.getBytes(StandardCharsets.UTF_8);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
// NoPadding 需要手动补零到16字节倍数
byte[] plainBytes = plainText.getBytes(StandardCharsets.UTF_8);
int blockSize = 16;
int paddedLen = ((plainBytes.length / blockSize) + 1) * blockSize;
byte[] padded = new byte[paddedLen];
System.arraycopy(plainBytes, 0, padded, 0, plainBytes.length);
// 剩余字节默认为0(ZeroPadding)
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(padded);
return Base64.getEncoder().encodeToString(encrypted);
}
}

225
zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/service/test/ResetUserPasswordTest.java

@ -1,225 +0,0 @@
package com.zdxt.service.test;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zdxt.enforcementcode.domain.EnterpriseUser;
import com.zdxt.enforcementcode.mapper.EnterpriseUserMapper;
import org.dromara.ZdxtAdminApplication;
import org.dromara.system.domain.SysUser;
import org.dromara.system.mapper.SysUserMapper;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import cn.dev33.satoken.context.mock.SaTokenContextMockUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.dev33.satoken.stp.StpUtil;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.satoken.utils.LoginHelper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 批量重置 enterprise_user 表用户密码
* <p>
* 密码加密规则: MD5(userName + password + salt)
* </p>
*/
@SpringBootTest(classes = ZdxtAdminApplication.class)
@AutoConfigureMockMvc
@ActiveProfiles({"dev", "dev-cust-zrb"})
@DisplayName("批量重置企业用户密码")
public class ResetUserPasswordTest {
@Autowired
private EnterpriseUserMapper enterpriseUserMapper;
@Autowired
private SysUserMapper sysUserMapper;
/** 重置后的明文密码 */
private static final String NEW_PASSWORD = "szsf@zfjd";
//private static final String NEW_PASSWORD = "Szsf@123";
private static final String TEST_CLIENT_ID = "e5cd7e4891bf95d1d19206ce24a7b32e";
@BeforeEach
public void setUp() {
// 初始化 Sa-Token 上下文(非 Web 环境下 StpUtil.login 需要)
SaTokenContextMockUtil.setMockContext();
// 设置模拟请求上下文(数据权限拦截器需要从 SaToken 上下文获取用户信息)
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
mockRequest.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockRequest));
// 构造测试用登录用户
LoginUser loginUser = new LoginUser();
loginUser.setUserId(1L);
loginUser.setUserType("sys_user");
loginUser.setTenantId("000000");
loginUser.setUsername("test_admin");
loginUser.setNickname("测试管理员");
loginUser.setDeptId("103");
loginUser.setDeptName("测试部门");
loginUser.setRolePermission(new HashSet<>());
loginUser.setMenuPermission(new HashSet<>());
// 模拟登录
SaLoginParameter param = new SaLoginParameter();
param.setExtra(LoginHelper.CLIENT_KEY, TEST_CLIENT_ID);
LoginHelper.login(loginUser, param);
}
@AfterEach
public void tearDown() {
try {
StpUtil.logout();
} catch (Exception ignored) {
}
RequestContextHolder.resetRequestAttributes();
SaTokenContextMockUtil.clearContext();
}
@DisplayName("批量重置 enterprise_user 所有用户密码为 szsf@zfjd")
@Test
public void resetAllEnterpriseUserPassword() {
/*-- 17608540150,weil45,"",15817307171,深圳农牧美益肉业有限公司,91440300662678582M,21
-- 17608557492,DGD440881199506180622,"",15817381630,深圳市深水龙岗污水处理有限公司,914403006820066603,12
-- 17608540121,smiledzb1982,"",13723454151,深圳康泰生物制品股份有限公司,91440300618837873J,10
-- 17608594890,A7271888,"",13751661770,深圳市里程食品有限公司,91440300738848091F,9
-- 17608565612,qintongzhen138,"",13823610579,欧姆龙电子部件(深圳)有限公司,914403007261811621,9
-- 17608601734,XP073215,"",15012671807,深圳市强达电路股份有限公司,91440300763461366N,9
-- 17608556724,33ef1970fd449676fa4bc197,"",13537602265,吉田拉链(深圳)有限公司公明工厂,91440300788302281W,8*/
// 1. 指定需要重置密码的用户ID列表
List<String> userIds = Arrays.asList(
// TODO: 在此填入需要重置的用户ID
"17608540150", "17608557492", "17608540121", "17608594890", "17608565612", "17608601734", "17608556724","2064275054386966530","2064274295779979265","17608629742"
);
// 2. 根据ID列表查询企业用户
List<EnterpriseUser> userList = enterpriseUserMapper.selectList(
new LambdaQueryWrapper<EnterpriseUser>()
.select(EnterpriseUser::getUserId, EnterpriseUser::getLoginName, EnterpriseUser::getUserName, EnterpriseUser::getSalt)
.in(EnterpriseUser::getUserId, userIds)
);
System.out.println("查询到企业用户总数: " + userList.size());
int successCount = 0;
int skipCount = 0;
for (EnterpriseUser user : userList) {
String loginName = user.getLoginName();
String salt = user.getSalt();
if (loginName == null || loginName.isBlank()) {
System.out.println("跳过: userId=" + user.getUserId() + ", loginName为空");
skipCount++;
continue;
}
if (salt == null || salt.isBlank()) {
System.out.println("跳过: userId=" + user.getUserId() + ", salt为空");
skipCount++;
continue;
}
// 3. 按规则生成新密码: MD5(loginName + newPassword + salt)
String newMd5Password = DigestUtil.md5Hex(loginName + NEW_PASSWORD + salt);
// 4. 更新密码
int rows = enterpriseUserMapper.update(null,
new LambdaUpdateWrapper<EnterpriseUser>()
.eq(EnterpriseUser::getUserId, user.getUserId())
.set(EnterpriseUser::getPassword, newMd5Password)
);
if (rows > 0) {
successCount++;
}
}
System.out.println("========== 重置完成 ==========");
System.out.println("成功: " + successCount + " 条");
System.out.println("跳过: " + skipCount + " 条");
System.out.println("新密码(明文): " + NEW_PASSWORD);
}
@DisplayName("批量重置 sys_user 指定用户密码为 szsf@zfjd")
@Test
public void resetSysUserPassword() {
// 1. 执法端的测试用户
List<Long> userIds = Arrays.asList(
// TODO: 在此填入需要重置的用户ID
7897L, 17608416858L,17608416876L, 17608416904L,17608416912L, 17608416926L,17608416927L, 17608416945L,17608417058L,17608642193L,17608502852L,17608515249L,17608497168L
);
// 监督端的报表测试用户
// userIds = Arrays.asList(
// // TODO: 在此填入需要重置的用户ID
// 17608417340L, 17608417451L,17608417716L, 17608614169L,17608614499L,7897L,17608634290L
// );
// 2. 根据ID列表查询系统用户
List<SysUser> userList = sysUserMapper.selectList(
new LambdaQueryWrapper<SysUser>()
.select(SysUser::getUserId, SysUser::getLoginName, SysUser::getSalt)
.in(SysUser::getUserId, userIds)
);
System.out.println("查询到系统用户总数: " + userList.size());
int successCount = 0;
int skipCount = 0;
for (SysUser user : userList) {
String loginName = user.getLoginName();
String salt = user.getSalt();
if (loginName == null || loginName.isBlank()) {
System.out.println("跳过: userId=" + user.getUserId() + ", loginName为空");
skipCount++;
continue;
}
if (salt == null || salt.isBlank()) {
System.out.println("跳过: userId=" + user.getUserId() + ", salt为空");
skipCount++;
continue;
}
// 3. 按规则生成新密码: MD5( + newPassword + salt)
String newMd5Password = DigestUtil.md5Hex(loginName + NEW_PASSWORD + salt);
// 4. 更新密码
int rows = sysUserMapper.update(null,
new LambdaUpdateWrapper<SysUser>()
.eq(SysUser::getUserId, user.getUserId())
.set(SysUser::getPassword, newMd5Password)
);
if (rows > 0) {
successCount++;
}
}
System.out.println("========== sys_user 重置完成 ==========");
System.out.println("成功: " + successCount + " 条");
System.out.println("跳过: " + skipCount + " 条");
System.out.println("新密码(明文): " + NEW_PASSWORD);
}
}
Loading…
Cancel
Save