Browse Source

密评接口优化

dev-V1.10.4
vincent 14 hours ago
parent
commit
01bccf76ae
  1. 28
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/CommonController.java
  2. 167
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/SysUserCommonController.java
  3. 26
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/ChangePhonenumberBo.java
  4. 22
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/QueryUserInfoBo.java
  5. 19
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ISysUserService.java
  6. 44
      zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java

28
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/CommonController.java

@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.service.ICryptoSensitiveHsmService;
@ -78,4 +79,31 @@ public class CommonController extends BaseController {
}
}
/**
* 同步操作日志密评
* @return
*/
@GetMapping("/syncIntegrityHashModifiedOperLogs")
public R<Void> syncIntegrityHashModifiedOperLogs() {
log.info("同步操作日志密评开始");
cryptoSensitiveHsmService.syncIntegrityHashModifiedOperLogs();
log.info("同步操作日志密评结束");
return R.ok("同步操作日志密评成功", null);
}
/**
* 同步操作日志密评
* @return
*/
@GetMapping("/initSysUserSensitiveData")
public R<Void> initSysUserSensitiveData() {
log.info("同步用户密评开始");
String loginName = "liufangyan1";
DataPermissionHelper.ignore(() -> cryptoSensitiveHsmService.initSysUserSensitiveData(loginName));
return R.ok("同步操作日志密评成功", null);
}
}

167
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/web/supervision/SysUserCommonController.java

@ -0,0 +1,167 @@
package com.zdxt.enforcementcode.controller.web.supervision;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zdxt.enforcementcode.common.util.CommonUtil;
import com.zdxt.enforcementcode.domain.bo.ChangePhonenumberBo;
import com.zdxt.enforcementcode.common.util.ZwSmsClient;
import com.zdxt.enforcementcode.domain.bo.QueryUserInfoBo;
import com.zdxt.enforcementcode.domain.bo.thirdparty.SIQMsgParamBo;
import com.zdxt.enforcementcode.service.thirdparty.ISIQService;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.SysUser;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ICryptoSensitiveHsmService;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.domain.vo.SysUserVo;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 监督端-通用接口
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/web/sysuser/common")
public class SysUserCommonController extends BaseController {
private final ISysUserService userService;
private final SysUserMapper sysUserMapper;
private final ICryptoSensitiveHsmService cryptoSensitiveHsmService;
/**
* 更改用户手机号码
* <p>密评双轨口径: phonenumber 列写 HSM 密文(SM4-CBC 随机 IV, 不可等值比较),
* crypto_remark_two 备份 AES 原逻辑值; 判重/查询均走备份轨道</p>
*
* @param bo 更改手机号参数(userId + phonenumber)
*/
@PostMapping("/changePhonenumber")
public R<Void> changePhonenumber(@Validated @RequestBody ChangePhonenumberBo bo) {
Long userId = bo.getUserId();
String phonenumber = bo.getPhonenumber();
SysUserVo user = userService.selectUserById(userId);
if (ObjectUtil.isNull(user)) {
return R.fail("用户不存在");
}
// 双轨判重: 走 crypto_remark_two 确定性备份轨道(HSM 密文随机 IV 无法等值比较)
if (!cryptoSensitiveHsmService.checkPhoneUnique(userId, phonenumber)) {
return R.fail("修改失败,该手机号已被其他账号使用");
}
// 双轨写入: phonenumber=HSM密文 + crypto_remark_two=AES备份(原子更新; HSM 失败保持原值并返回失败)
if (!cryptoSensitiveHsmService.savePhoneDualTrack(userId, phonenumber)) {
return R.fail("手机号修改失败,请稍后重试");
}
// mobile_number 列(市认证扩展字段, AES 形态)同步更新, 避免两手机号字段口径不一致
String aesPhone = cryptoSensitiveHsmService.normalizePhoneToAes(phonenumber);
if (StringUtils.isNotBlank(aesPhone)) {
sysUserMapper.update(null, Wrappers.<SysUser>lambdaUpdate()
.eq(SysUser::getUserId, userId)
.set(SysUser::getMobileNumber, aesPhone));
}
// 完整性摘要按库中实际值刷新(摘要口径与库中值一致, 避免完整性校验误报篡改)
try {
SysUser updated = sysUserMapper.selectById(userId);
cryptoSensitiveHsmService.saveIntegrityHash("sys_user", String.valueOf(userId),
"password,phonenumber,certificateNumber",
updated.getPassword() != null ? updated.getPassword() : "",
updated.getPhonenumber() != null ? updated.getPhonenumber() : "",
updated.getCertificateNumber() != null ? updated.getCertificateNumber() : "");
} catch (Exception e) {
log.warn("手机号修改后完整性摘要刷新失败, userId={}", userId, e);
}
return R.ok();
}
/**
* 根据手机号查询用户
* <p>密评双轨口径: phonenumber 已为 HSM 密文(随机 IV 不可等值比较),
* 归一化后走 crypto_remark_two 备份轨道等值查询, 兼容未双轨化存量行</p>
*
* @param phonenumber 手机号(明文, 11 位)
*/
@GetMapping("/getUserByPhonenumber")
public R<Map<String, Object>> getUserByPhonenumber(
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
@RequestParam String phonenumber) {
SysUserVo user = userService.selectUserByPhonenumber(phonenumber);
if (ObjectUtil.isNull(user)) {
return R.fail("未找到该手机号对应的用户");
}
Map<String, Object> data = new HashMap<>();
data.put("userId", user.getUserId());
data.put("userName", user.getUserName());
data.put("loginName", user.getLoginName());
data.put("nickName", user.getNickName());
data.put("deptId", user.getDeptId());
data.put("status", user.getStatus());
// 双轨解密展示(备份轨道优先, 兼容明文/AES/HSM 三种存量形态)
String phone = CommonUtil.decryptPhoneForDisplay(user.getPhonenumber(), user.getCryptoRemarkTwo());
data.put("phonenumber", phone != null ? phone : "");
return R.ok(data);
}
/**
* 根据用户名称 + 手机号查询用户信息
* <p>密评双轨口径: 手机号条件走 crypto_remark_two 备份轨道等值查询; 用户名称模糊匹配;
* 存量手机号无唯一约束可能重复, 返回全部匹配用户(按更新时间倒序)</p>
*
* @param bo 查询参数(userName + phonenumber, 至少填写一项)
*/
@PostMapping("/queryUserInfo")
public R<List<Map<String, Object>>> queryUserInfo(@Validated @RequestBody QueryUserInfoBo bo) {
if (StringUtils.isBlank(bo.getUserName()) && StringUtils.isBlank(bo.getPhonenumber())) {
return R.fail("用户名称与手机号至少填写一项");
}
List<SysUserVo> users = userService.selectUserListByCondition(bo.getUserName(), bo.getPhonenumber());
if (ObjectUtil.isEmpty(users)) {
return R.fail("未找到符合条件的用户");
}
List<Map<String, Object>> rows = new ArrayList<>(users.size());
for (SysUserVo user : users) {
rows.add(buildUserRow(user));
}
return R.ok(rows);
}
/**
* 组装用户返回数据(手工挑字段, 规避 password/cryptoRemark* 等敏感字段外泄)
*/
private Map<String, Object> buildUserRow(SysUserVo user) {
Map<String, Object> data = new HashMap<>();
data.put("userId", user.getUserId());
data.put("userName", user.getUserName());
data.put("loginName", user.getLoginName());
data.put("nickName", user.getNickName());
data.put("deptId", user.getDeptId());
data.put("status", user.getStatus());
data.put("lawCertificateNum", user.getLawCertificateNum());
// 双轨解密展示(备份轨道优先, 兼容明文/AES/HSM 三种存量形态)
String phone = CommonUtil.decryptPhoneForDisplay(user.getPhonenumber(), user.getCryptoRemarkTwo());
data.put("phonenumber", phone != null ? phone : "");
return data;
}
}

26
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/ChangePhonenumberBo.java

@ -0,0 +1,26 @@
package com.zdxt.enforcementcode.domain.bo;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
/**
* 监督端-更改用户手机号参数
*/
@Data
public class ChangePhonenumberBo {
/**
* 目标用户ID
*/
@NotNull(message = "用户ID不能为空")
private Long userId;
/**
* 新手机号(明文, 11位)
*/
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phonenumber;
}

22
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/bo/QueryUserInfoBo.java

@ -0,0 +1,22 @@
package com.zdxt.enforcementcode.domain.bo;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
/**
* 监督端-用户信息组合查询参数
*/
@Data
public class QueryUserInfoBo {
/**
* 用户名称(模糊匹配, 与手机号至少填写一项)
*/
private String userName;
/**
* 手机号(明文, 11位, 与用户名称至少填写一项)
*/
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phonenumber;
}

19
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/ISysUserService.java

@ -266,6 +266,25 @@ public interface ISysUserService {
*/
List<SysUserVo> selectUserListByLoginName(String loginName);
/**
* 通过手机号查询用户列表
* <p>存量数据手机号无唯一约束可能重复, 返回全部匹配用户(按更新时间倒序)</p>
*
* @param phonenumber 手机号
* @return 用户列表信息
*/
List<SysUserVo> selectUserListByPhonenumber(String phonenumber);
/**
* 通过用户名称 + 手机号组合条件查询用户列表
* <p>两个条件均可选但至少一项: 用户名称模糊匹配; 手机号走密评双轨备份轨道等值查询</p>
*
* @param userName 用户名称(可空)
* @param phonenumber 手机号(可空)
* @return 用户列表信息(按更新时间倒序)
*/
List<SysUserVo> selectUserListByCondition(String userName, String phonenumber);
/**
* 获取当前用户信息
*

44
zdxt-modules/zdxt-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java

@ -854,6 +854,50 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
return baseMapper.selectVoList(lqw);
}
/**
* 通过手机号查询用户
*
* @param phonenumber 手机号
* @return 用户对象信息
*/
@Override
public List<SysUserVo> selectUserListByPhonenumber(String phonenumber) {
if (StringUtils.isBlank(phonenumber)) {
return new ArrayList<>();
}
// 与单条方法同口径: HSM 密文不可等值比较, 归一化后走 crypto_remark_two 备份轨道(兼容未双轨化存量行)
String aesForm = cryptoSensitiveHsmService.normalizePhoneToAes(phonenumber);
if (StringUtils.isBlank(aesForm)) {
return new ArrayList<>();
}
return baseMapper.selectVoList(new LambdaQueryWrapper<SysUser>()
.and(w -> w.eq(SysUser::getCryptoRemarkTwo, aesForm)
.or().eq(SysUser::getPhonenumber, aesForm))
.orderByDesc(SysUser::getUpdateTime)
.orderByDesc(SysUser::getUserId));
}
@Override
public List<SysUserVo> selectUserListByCondition(String userName, String phonenumber) {
if (StringUtils.isBlank(userName) && StringUtils.isBlank(phonenumber)) {
return new ArrayList<>();
}
LambdaQueryWrapper<SysUser> lqw = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(phonenumber)) {
// 手机号走双轨备份轨道(HSM 密文随机 IV 不可等值比较); 归一化失败宁可返回空, 避免条件失效全表扫
String aesForm = cryptoSensitiveHsmService.normalizePhoneToAes(phonenumber);
if (StringUtils.isBlank(aesForm)) {
return new ArrayList<>();
}
lqw.and(w -> w.eq(SysUser::getCryptoRemarkTwo, aesForm)
.or().eq(SysUser::getPhonenumber, aesForm));
}
lqw.like(StringUtils.isNotBlank(userName), SysUser::getUserName, userName);
lqw.orderByDesc(SysUser::getUpdateTime)
.orderByDesc(SysUser::getUserId);
return baseMapper.selectVoList(lqw);
}
/**
* 获取当前用户信息
*

Loading…
Cancel
Save