新增贷款定价敏感字段加解密服务

This commit is contained in:
wkc
2026-03-30 10:51:44 +08:00
parent d7c305b26c
commit b16a08eb1a
5 changed files with 174 additions and 0 deletions

View File

@@ -103,3 +103,7 @@ xss:
security: security:
password-transfer: password-transfer:
key: "1234567890abcdef" key: "1234567890abcdef"
loan-pricing:
sensitive:
key: "1234567890abcdef"

View File

@@ -0,0 +1,46 @@
package com.ruoyi.loanpricing.service;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service
public class LoanPricingSensitiveDisplayService
{
public String maskCustName(String custName)
{
if (!StringUtils.hasText(custName))
{
return custName;
}
if (custName.contains("公司") && custName.length() > 4)
{
return custName.substring(0, 2) + "*".repeat(custName.length() - 4) + custName.substring(custName.length() - 2);
}
if (custName.length() == 1)
{
return custName;
}
return custName.substring(0, 1) + "*".repeat(custName.length() - 1);
}
public String maskIdNum(String idNum)
{
if (!StringUtils.hasText(idNum))
{
return idNum;
}
if (idNum.startsWith("91") && idNum.length() == 18)
{
return idNum.substring(0, 2) + "*".repeat(13) + idNum.substring(idNum.length() - 3);
}
if (idNum.matches("\\d{17}[\\dXx]"))
{
return idNum.substring(0, 4) + "*".repeat(8) + idNum.substring(idNum.length() - 4);
}
if (idNum.length() > 5)
{
return idNum.substring(0, 2) + "*".repeat(idNum.length() - 5) + idNum.substring(idNum.length() - 3);
}
return "*".repeat(idNum.length());
}
}

View File

@@ -0,0 +1,68 @@
package com.ruoyi.loanpricing.service;
import com.ruoyi.common.exception.ServiceException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@Service
public class SensitiveFieldCryptoService
{
private final String key;
public SensitiveFieldCryptoService(@Value("${loan-pricing.sensitive.key:}") String key)
{
this.key = key;
}
public String encrypt(String plainText)
{
validateKey();
if (!StringUtils.hasText(plainText))
{
return plainText;
}
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"));
return Base64.getEncoder().encodeToString(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)));
}
catch (Exception ex)
{
throw new ServiceException("贷款定价敏感字段加密失败");
}
}
public String decrypt(String cipherText)
{
validateKey();
if (!StringUtils.hasText(cipherText))
{
return cipherText;
}
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"));
return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)), StandardCharsets.UTF_8);
}
catch (Exception ex)
{
throw new ServiceException("贷款定价敏感字段解密失败");
}
}
private void validateKey()
{
if (!StringUtils.hasText(key))
{
throw new IllegalStateException("loan-pricing.sensitive.key 未配置");
}
}
}

View File

@@ -0,0 +1,24 @@
package com.ruoyi.loanpricing.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class LoanPricingSensitiveDisplayServiceTest
{
private final LoanPricingSensitiveDisplayService displayService = new LoanPricingSensitiveDisplayService();
@Test
void shouldMaskPersonalNameAndIdNum()
{
assertEquals("张*", displayService.maskCustName("张三"));
assertEquals("1101********1234", displayService.maskIdNum("110101199001011234"));
}
@Test
void shouldMaskCorporateNameAndCreditCode()
{
assertEquals("测试****公司", displayService.maskCustName("测试科技有限公司"));
assertEquals("91*************00X", displayService.maskIdNum("91110000100000000X"));
}
}

View File

@@ -0,0 +1,32 @@
package com.ruoyi.loanpricing.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class SensitiveFieldCryptoServiceTest
{
@Test
void shouldEncryptAndDecryptCustNameAndIdNum()
{
SensitiveFieldCryptoService service = new SensitiveFieldCryptoService("1234567890abcdef");
String nameCipher = service.encrypt("张三");
String idNumCipher = service.encrypt("110101199001011234");
assertNotEquals("张三", nameCipher);
assertNotEquals("110101199001011234", idNumCipher);
assertEquals("张三", service.decrypt(nameCipher));
assertEquals("110101199001011234", service.decrypt(idNumCipher));
}
@Test
void shouldRejectBlankKeyConfiguration()
{
SensitiveFieldCryptoService service = new SensitiveFieldCryptoService("");
assertThrows(IllegalStateException.class, () -> service.encrypt("张三"));
}
}