Java地址脱敏实操案例
地址脱敏在实际开发中非常常见,主要目的是保护用户隐私,以下是几种常见的地址脱敏方案和完整代码实现。

基础地址脱敏方案
public class AddressDesensitization {
/**
* 地址脱敏 - 保留前几个字符和后几个字符
* @param address 原始地址
* @param frontKeep 前面保留字符数
* @param endKeep 后面保留字符数
* @return 脱敏后的地址
*/
public static String desensitizeAddress(String address, int frontKeep, int endKeep) {
if (address == null || address.isEmpty()) {
return address;
}
int length = address.length();
if (length <= frontKeep + endKeep) {
// 如果地址太短,全部用星号代替
return address.replaceAll(".", "*");
}
String front = address.substring(0, frontKeep);
String end = address.substring(length - endKeep);
String middle = address.substring(frontKeep, length - endKeep);
// 用星号替换中间部分
String maskedMiddle = middle.replaceAll(".", "*");
return front + maskedMiddle + end;
}
public static void main(String[] args) {
String address = "北京市朝阳区建国路88号SOHO现代城A座1508室";
// 保留前2个字符和后3个字符
String masked1 = desensitizeAddress(address, 2, 3);
System.out.println("脱敏结果1: " + masked1);
// 输出: 北京市**************************1508室
// 保留省市区信息
String masked2 = desensitizeAddress(address, 6, 10);
System.out.println("脱敏结果2: " + masked2);
// 输出: 北京市朝阳区**************A座1508室
}
}
智能地址脱敏(保留省市信息)
public class SmartAddressDesensitization {
// 省级行政区划关键词
private static final Set<String> PROVINCE_KEYWORDS = new HashSet<>(Arrays.asList(
"省", "自治区", "自治区", "特别行政区"
));
// 市级行政区划关键词
private static final Set<String> CITY_KEYWORDS = new HashSet<>(Arrays.asList(
"市", "区", "县", "自治州", "盟", "地区"
));
// 区县级关键词
private static final Set<String> DISTRICT_KEYWORDS = new HashSet<>(Arrays.asList(
"区", "县", "县级市", "旗", "自治旗"
));
/**
* 智能地址脱敏 - 自动识别并保留省市信息
*/
public static String smartDesensitize(String address) {
if (address == null || address.isEmpty()) {
return address;
}
// 查找省级位置
int provinceEnd = findKeywordEnd(address, PROVINCE_KEYWORDS);
if (provinceEnd <= 0) {
// 如果没有省级关键词,使用基础方法
return desensitizeAddress(address, 1, 1);
}
// 查找市级位置
int cityEnd = findKeywordEnd(address, CITY_KEYWORDS, provinceEnd);
if (cityEnd <= 0) {
cityEnd = provinceEnd;
}
// 查找区县级位置
int districtEnd = findKeywordEnd(address, DISTRICT_KEYWORDS, cityEnd);
if (districtEnd <= 0) {
districtEnd = cityEnd;
}
// 保留省市区县信息,后续地址脱敏
String keepPart = address.substring(0, districtEnd + 1);
String maskPart = address.substring(districtEnd + 1);
// 对后续地址进行脱敏
String maskedSuffix = maskAddressSuffix(maskPart);
return keepPart + maskedSuffix;
}
private static int findKeywordEnd(String text, Set<String> keywords) {
return findKeywordEnd(text, keywords, 0);
}
private static int findKeywordEnd(String text, Set<String> keywords, int startPos) {
for (String keyword : keywords) {
int index = text.indexOf(keyword, startPos);
if (index >= 0) {
return index + keyword.length() - 1;
}
}
return -1;
}
private static String maskAddressSuffix(String suffix) {
if (suffix.isEmpty()) {
return "";
}
int length = suffix.length();
if (length <= 2) {
return suffix.replaceAll(".", "*");
}
// 保留第一个和最后一个字符
String first = suffix.substring(0, 1);
String last = suffix.substring(length - 1);
String middle = suffix.substring(1, length - 1);
return first + middle.replaceAll(".", "*") + last;
}
public static void main(String[] args) {
// 测试各种地址格式
String[] testAddresses = {
"北京市朝阳区建国路88号SOHO现代城A座1508室",
"上海市浦东新区世纪大道100号上海环球金融中心",
"广东省广州市天河区天河路385号太古汇",
"浙江省杭州市西湖区西溪路556号阿里中心",
"四川省成都市武侯区天府大道北段1700号"
};
for (String address : testAddresses) {
String masked = smartDesensitize(address);
System.out.println("原始: " + address);
System.out.println("脱敏: " + masked);
System.out.println("---");
}
}
}
使用正则表达式脱敏
public class RegexAddressDesensitization {
/**
* 使用正则表达式进行地址脱敏
*/
public static String regexDesensitize(String address) {
if (address == null || address.isEmpty()) {
return address;
}
// 脱敏规则:保留省市区信息,后续地址替换为*
// 匹配省市区县的模式
String pattern = "(.{0,10}(省|自治区|市|区|县|自治州|旗))(.+)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(address);
if (m.find()) {
String location = m.group(1);
String detail = m.group(3);
String maskedDetail = detail.replaceAll(".", "*");
return location + maskedDetail;
}
// 如果没有匹配到省市信息,使用通用脱敏
return desensitizeAddress(address, 2, 2);
}
/**
* 通用地址脱敏工具方法
*/
public static String desensitizeAddress(String address, int frontKeep, int endKeep) {
if (address == null || address.isEmpty()) {
return address;
}
int length = address.length();
if (length <= frontKeep + endKeep) {
return address.replaceAll(".", "*");
}
String front = address.substring(0, frontKeep);
String end = address.substring(length - endKeep);
String middle = address.substring(frontKeep, length - endKeep);
String maskedMiddle = middle.replaceAll(".", "*");
return front + maskedMiddle + end;
}
public static void main(String[] args) {
String[] testAddresses = {
"北京市朝阳区建国路88号SOHO现代城",
"上海市浦东新区张江高科技园区",
"广东省深圳市南山区科技园南区",
"浙江省杭州市西湖区文三路478号"
};
for (String address : testAddresses) {
String masked = regexDesensitize(address);
System.out.println("原始: " + address);
System.out.println("脱敏: " + masked);
System.out.println("---");
}
}
}
完整的生产级脱敏工具类
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
public final class AddressMaskingUtil {
private AddressMaskingUtil() {
// 工具类禁止实例化
}
// 配置常量
private static final Set<String> PROVINCE_KEYWORDS = new HashSet<>(Arrays.asList(
"省", "自治区", "特别行政区"
));
private static final Set<String> CITY_KEYWORDS = new HashSet<>(Arrays.asList(
"市", "自治州", "盟", "地区"
));
private static final Set<String> DISTRICT_KEYWORDS = new HashSet<>(Arrays.asList(
"区", "县", "县级市", "旗", "自治旗", "林区", "特区"
));
public enum MaskingStrategy {
FULL_MASK, // 完全掩码
PRESERVE_PROVINCE, // 保留省级
PRESERVE_CITY, // 保留市级
PRESERVE_DISTRICT, // 保留区县级
CUSTOM // 自定义
}
/**
* 地址脱敏主方法
* @param address 原始地址
* @param strategy 脱敏策略
* @return 脱敏后的地址
*/
public static String mask(String address, MaskingStrategy strategy) {
if (address == null || address.isEmpty()) {
return address;
}
switch (strategy) {
case FULL_MASK:
return fullMask(address);
case PRESERVE_PROVINCE:
return preserveProvince(address);
case PRESERVE_CITY:
return preserveCity(address);
case PRESERVE_DISTRICT:
return preserveDistrict(address);
case CUSTOM:
return customMask(address);
default:
return fullMask(address);
}
}
/**
* 完全掩码
*/
private static String fullMask(String address) {
return address.replaceAll(".", "*");
}
/**
* 保留省级信息
*/
private static String preserveProvince(String address) {
int endIndex = findProvinceEnd(address);
if (endIndex >= 0) {
return address.substring(0, endIndex + 1) +
maskRest(address, endIndex + 1);
}
return fullMask(address);
}
/**
* 保留市级信息
*/
private static String preserveCity(String address) {
int provinceEnd = findProvinceEnd(address);
int cityEnd = findCityEnd(address, provinceEnd + 1);
if (cityEnd >= 0) {
return address.substring(0, cityEnd + 1) +
maskRest(address, cityEnd + 1);
} else if (provinceEnd >= 0) {
return address.substring(0, provinceEnd + 1) +
maskRest(address, provinceEnd + 1);
}
return fullMask(address);
}
/**
* 保留区县级信息
*/
private static String preserveDistrict(String address) {
int provinceEnd = findProvinceEnd(address);
int cityEnd = findCityEnd(address, provinceEnd + 1);
int districtEnd = findDistrictEnd(address, cityEnd + 1);
if (districtEnd >= 0) {
return address.substring(0, districtEnd + 1) +
maskRest(address, districtEnd + 1);
} else if (cityEnd >= 0) {
return address.substring(0, cityEnd + 1) +
maskRest(address, cityEnd + 1);
} else if (provinceEnd >= 0) {
return address.substring(0, provinceEnd + 1) +
maskRest(address, provinceEnd + 1);
}
return fullMask(address);
}
/**
* 自定义脱敏(示例:保留前几个和后几个字符)
*/
private static String customMask(String address) {
if (address.length() <= 4) {
return fullMask(address);
}
return address.substring(0, 2) +
address.substring(2, address.length() - 2).replaceAll(".", "*") +
address.substring(address.length() - 2);
}
/**
* 掩码剩余部分(保留首尾字符)
*/
private static String maskRest(String address, int startIndex) {
if (startIndex >= address.length()) {
return "";
}
String rest = address.substring(startIndex);
if (rest.length() <= 2) {
return rest.replaceAll(".", "*");
}
return rest.substring(0, 1) +
rest.substring(1, rest.length() - 1).replaceAll(".", "*") +
rest.substring(rest.length() - 1);
}
/**
* 查找省级关键词结束位置
*/
private static int findProvinceEnd(String address) {
for (String keyword : PROVINCE_KEYWORDS) {
int index = address.indexOf(keyword);
if (index >= 0) {
return index + keyword.length() - 1;
}
}
// 查找直辖市(北京、上海、天津、重庆)
if (address.startsWith("北京") || address.startsWith("上海") ||
address.startsWith("天津") || address.startsWith("重庆")) {
// 直辖市后面通常接"市"
int shiIndex = address.indexOf("市", 2);
if (shiIndex >= 0 && shiIndex < 3) {
return shiIndex;
}
}
return -1;
}
/**
* 查找市级关键词结束位置
*/
private static int findCityEnd(String address, int fromIndex) {
for (String keyword : CITY_KEYWORDS) {
int index = address.indexOf(keyword, fromIndex);
if (index >= 0) {
return index + keyword.length() - 1;
}
}
return -1;
}
/**
* 查找区县级关键词结束位置
*/
private static int findDistrictEnd(String address, int fromIndex) {
for (String keyword : DISTRICT_KEYWORDS) {
int index = address.indexOf(keyword, fromIndex);
if (index >= 0) {
return index + keyword.length() - 1;
}
}
return -1;
}
public static void main(String[] args) {
String address = "北京市朝阳区建国路88号SOHO现代城A座1508室";
System.out.println("原始地址: " + address);
System.out.println("完全掩码: " + mask(address, MaskingStrategy.FULL_MASK));
System.out.println("保留省级: " + mask(address, MaskingStrategy.PRESERVE_PROVINCE));
System.out.println("保留市级: " + mask(address, MaskingStrategy.PRESERVE_CITY));
System.out.println("保留区县: " + mask(address, MaskingStrategy.PRESERVE_DISTRICT));
System.out.println("自定义: " + mask(address, MaskingStrategy.CUSTOM));
// 测试其他地址格式
String[] addresses = {
"上海市浦东新区世纪大道100号",
"广东省广州市天河区天河路385号",
"四川省成都市武侯区人民南路四段11号"
};
System.out.println("\n--- 多地址测试 ---");
for (String addr : addresses) {
System.out.println("原始: " + addr);
System.out.println("脱敏: " + mask(addr, MaskingStrategy.PRESERVE_DISTRICT));
System.out.println("---");
}
}
}
使用示例
public class AddressMaskingDemo {
public static void main(String[] args) {
// 1. 基础脱敏
String addr1 = AddressDesensitization.desensitizeAddress("北京市朝阳区建国路88号", 2, 3);
System.out.println("基础脱敏: " + addr1);
// 2. 智能脱敏
String addr2 = SmartAddressDesensitization.smartDesensitize("广东省深圳市南山区科技园南区A栋");
System.out.println("智能脱敏: " + addr2);
// 3. 工具类脱敏
String addr3 = AddressMaskingUtil.mask("四川省成都市武侯区人民南路四段11号",
AddressMaskingUtil.MaskingStrategy.PRESERVE_DISTRICT);
System.out.println("工具类脱敏: " + addr3);
}
}
-
脱敏策略选择:
- 完全掩码:适用于不需要显示任何地址信息
- 保留省市:适用于需要显示大致的地区信息
- 保留区县:适用于需要显示较详细的区域信息
- 自定义:根据需要保留特定部分
-
边界情况处理:
- 空地址或null
- 短地址(少于保留字符数)
- 不同的地址格式和命名习惯
-
性能优化:
- 缓存常用脱敏结果
- 预编译正则表达式
- 避免在循环中频繁创建Pattern对象
-
安全性考虑:
- 确保脱敏后无法还原原始地址
- 不同场景使用不同的脱敏强度
- 结合业务需求调整脱敏规则
根据你的具体业务需求,可以选择合适的脱敏方案或组合使用。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播