外观
脱敏与编码
展示脱敏、证件格式检查、摘要和加解密解决不同问题。本页先给出展示与信息处理用法,再说明旧协议接口的实际支持范围;方法名为 encrypt 不一定表示执行了加密。
| 你的目的 | 选择与结果 |
|---|---|
| 页面上隐藏手机号等字段 | 脱敏展示,得到展示文本;保留原数据 |
| 从大陆身份证文本提取生日 | 严格解析示例,得到生日或空结果 |
| 将文本转为 Base64,或计算固定算法摘要 | 编码与摘要,分别使用 BASE64 或 SHA_256 等入口 |
| 对接已存在的加密协议 | 先核对实际支持范围中的签名与限制 |
需要为新敏感数据设计加密方案时,本模块当前没有可直接推荐的完整入口;不要直接套用旧协议接口。下文保留这些接口的行为,便于维护已有调用。
脱敏展示
DesensitizeUtil 与 DesensitizeRule 位于 com.own.constant.util.desensitize:
java
import com.own.constant.util.desensitize.DesensitizeRule;
import com.own.constant.util.desensitize.DesensitizeUtil;
String masked = DesensitizeUtil.build("13812345678", DesensitizeRule.Template.PHONE);
System.out.println(masked); // 138****5678| DesensitizeRule.Template | 规则 |
|---|---|
NAME | 保留首字符,其余按长度替换为星号 |
PHONE | 匹配手机号格式时保留前三、后四位 |
PHONE_PREFIX | 匹配手机号格式时仅保留后四位 |
ID_CARD_FOUR | 对匹配的 15/18 位文本保留前六位和最后一位,名称中的 FOUR 不表示保留后四位 |
STAR_ALL / PASSWORD | 全部替换星号,两者为同一规则实例 |
API_SK | 长度大于 4 时保留前四位,否则全部替换 |
EMAIL | 对匹配的邮箱保留本地部分首字符与域名,其余用星号替换 |
规则对 null、空或全空白原样返回,长度按 UTF-16 code unit 计算。PHONE、EMAIL、ID_CARD_FOUR 对不符合各自格式的文本可能原样返回,因此不能把调用成功视为所有输入都已隐藏;应用需要先确定字段格式,或使用适合自身输入的规则。
DesensitizeRule<T> 继承 Function<T,T>,可以提供自定义 Lambda。DesensitizeUtil.build(content, rule) 对 null content 返回 null,其他内容调用规则。
另有 build(object, getter, setter, rule) 就地修改对象,以及 build(list, getter, setter, rule) 通过 parallelStream() 并行修改所有元素。列表和函数需要非 null,重复引用对象或共享可变状态可能产生竞态。展示处理宜对独立 DTO 操作,避免脱敏后覆盖仍需保存的原数据。
证件格式与信息提取
IdCardModel、IdCardUtil 位于 com.own.constant.util.verification。它们计算格式、校验位和可提取字段,不连接身份核验服务。
大陆身份证模型
需要从外部输入提取生日时,先裁剪空白、检查长度及校验位,再补充严格日期和地区判断。以下 example.CardBirthday 提供可直接调用的解析方法,不把结果视为实名核验:
java
package example;
import com.own.constant.util.verification.IdCardUtil;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
public class CardBirthday {
public static Optional<LocalDate> parse(String input) {
if (input == null) {
return Optional.empty();
}
String card = IdCardUtil.convert15CardTo18(input.trim());
if (card == null || !IdCardUtil.validateIdCard18(card)
|| IdCardUtil.getProvinceByIdCard(card) == null) {
return Optional.empty();
}
try {
LocalDate birthday = LocalDate.parse(card.substring(6, 14),
DateTimeFormatter.BASIC_ISO_DATE);
return birthday.isAfter(LocalDate.now()) ? Optional.empty() : Optional.of(birthday);
} catch (java.time.format.DateTimeParseException e) {
return Optional.empty();
}
}
}调用 CardBirthday.parse(input) 后,通过上述检查的输入得到 Optional<LocalDate>;空输入、格式或校验位错误、未知省份、非法生日及未来生日得到 Optional.empty()。调用方据此展示生日或提示格式错误。
new IdCardModel(text) 会先转换 15 位旧格式,再执行校验与解析。有效时 isAvailable() 为 true,getIdCardInfo() 提供 gender、age、birthday、year/month/day、province 的 getter;无效时返回默认信息对象,性别为 NULL、数字为 0、其他为 null。
validateIdCard18 只检查前 17 位数字与校验位,不检查生日或地区。IdCardModel 的日期 Formatter 使用默认解析规则,部分不合理日期可能被调整,另一些会抛 DateTimeParseException;不能保证任意非法输入都只得到 available=false。
身份证字段与兼容方法速查
| 入口 | 实际范围与限制 |
|---|---|
convert15CardTo18 | 15 位固定补 19 年代并计算校验位;18 位原样返回;其他长度为 null |
verificationIdCard15 | 检查数字、地区并经转换校验 |
validateCard | trim 后依次尝试大陆和港澳台分支;不补足 18 位生日校验 |
validDate(year, month, day) | 年份要求大于等于 1930 且小于当前年份,当前年份日期会被拒绝 |
getAgeByIdCard | 当前年份减出生年份,不按生日计算周岁 |
getBirth/Year/Month/DateByIdCard | 按号码提取日期信息,不统一执行完整校验;空或非法输入返回空字符串、0 或 null,数字解析也可抛异常 |
getGenderByIdCard / getProvinceByIdCard | 从数字位或内置地区表取值,未知分别为 NULL / null;非法性别数字可抛异常 |
IdCardInfo.builder() 只是构造数据,不检查字段一致性。准确周岁应在取得有效生日后按应用规则计算。
港澳台兼容入口
IdCardModel 的前置转换只接受 15/18 位,因此不适用于港澳台证件。IdCardUtil.verificationIdCard10 返回地区、性别、字符串校验结果三个元素,无法识别返回 null。
澳门分支没有填充第三项,正则字符类还接受 |,因此不能当作有效验证结果;台湾分支允许小写进入,但内部字母映射未归一化,可能抛空指针;香港入口也保留特殊号码兼容限制。verificationTwCard、validateHKCard 假定输入格式满足要求,不适合对任意外部文本直接调用。
编码与摘要
统一入口 com.own.constant.util.encrypt.EncryptUtil 暴露共享算法实例。大多数字段的声明类型为 BaseEncryptUtil,额外的实现方法并不都能直接从字段调用。
java
import com.own.constant.util.encrypt.EncryptUtil;
String encoded = EncryptUtil.BASE64.encrypt("hello"); // aGVsbG8=
String decoded = EncryptUtil.BASE64.decrypt(encoded); // hello
String digest = EncryptUtil.SHA_256.encrypt("payload");| 入口 | 结果与边界 |
|---|---|
BASE64.encrypt(String) | UTF-8 标准 Base64;不是保密加密 |
BASE64.decrypt(String) | Base64 解码后使用默认字符集构造文本;非法 Base64 抛异常 |
MD5.encrypt(String) | 32 位小写十六进制摘要 |
SHA_1/SHA_256.encrypt(String) | 对应算法的小写十六进制摘要 |
SHA.encrypt(String, algorithm) | 自选 MessageDigest 算法;算法不存在时记录日志并返回原文 |
LOOP.encrypt/decrypt(String) | 对每个 char 与字符 t 做 XOR,仅为可逆混淆 |
对象重载先用 Fastjson2 序列化为 JSON;带 Class 的解码重载再反序列化。调用方要区分 String 与 Object 重载,尤其是静态类型为 Object 的字符串会先变成 JSON 字符串。摘要不提供解密或密码存储协议。
com.own.constant.util.encrypt.util.BcdUtil.str2Bcd 对奇数长度前补 0;bcd2Str 会去掉一个前导 0。它按十进制数字输出半字节,不能作为任意十六进制往返工具,空数组解码也会抛异常。
加密接口的实际支持范围
BaseEncryptUtil 的默认 buildKey() 返回 null,默认字符串 encrypt/decrypt 原文返回。未覆盖的重载不会抛“不支持”,例如对 MD5 调用 decrypt 或对 AES_128 调用 encrypt 可能直接取得原文。先确认实现支持的签名,再判断调用结果。
AES/AES_128 的共享 Cipher 不适合并发调用,RSA_PUBLIC 的私钥随包分发,SHA_256_WITH_RSA 还缺少配套验签。原文回退、共享状态和密钥问题需要调用方解决,不能只通过“可往返”验证适用性。
旧协议接口支持矩阵与密钥格式
密钥对象 CustomEncryptKey 位于 com.own.constant.util.encrypt.model,builder 可设置 publicKey、privateKey、pk、secret、iv 和 isContent。构造不校验字段组合;isContent 默认 false,当前算法未读取它。
| 入口与有效调用 | 当前实现限制 |
|---|---|
HMAC_SHA_1/HMAC_SHA_256.encrypt(content, key) | content 用作 HMAC 密钥,key.secret 用作数据,与参数名称直觉相反;结果为 Base64,异常为空字符串 |
AES.encrypt/decrypt(String) | AES/CBC/PKCS5Padding,静态 Cipher、固定 IV;key 用固定种子交给默认 SecureRandom 生成,不保证跨进程/Provider 重现;捕获的加解密异常返回空字符串 |
AES_128.decrypt(content, key) | secret、iv 和密文均按 Base64 解码,使用 AES/CBC/PKCS7Padding;模块没有注册对应扩展 Provider,失败为空字符串;未实现有效 encrypt |
RSA.buildKey()、RSA.encrypt/decrypt(content, key) | buildKey 生成 1024 位密钥;公钥 X.509、私钥 PKCS#8,均为 Base64;使用默认 RSA transformation,不分段,JCA 异常包装为普通 RuntimeException |
RSA_PUBLIC.encrypt/decrypt(String) | 私钥随源码和 JAR 分发,不提供秘密密钥隔离 |
SHA_256_WITH_RSA.encrypt(content, key) | 先将 publicKey 文本字节当作 PKCS#8 私钥解析,常规输入通常在此失败并返回空字符串;没有配套验签入口 |
EncryptUtil 字段是可重新赋值的 public static,应用不要在请求处理中替换实例。非法 Base64、null 等也可能抛出算法 catch 范围外的异常,不能把所有失败都统一判断为空串。
PrivateKeyUtil.getPrivateKey(filename) / getPrivateKeyByText(text) 支持 PKCS#8 RSA PEM 或纯 Base64;只移除 BEGIN PRIVATE KEY 标记,不支持 BEGIN RSA PRIVATE KEY 的 PKCS#1 文本。文件、Base64 和密钥格式异常会直接抛出,不自动生成备用密钥。