Java地址脱敏案例如何实操

wen java案例 21

Java地址脱敏实操案例

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

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);
    }
}
  1. 脱敏策略选择

    • 完全掩码:适用于不需要显示任何地址信息
    • 保留省市:适用于需要显示大致的地区信息
    • 保留区县:适用于需要显示较详细的区域信息
    • 自定义:根据需要保留特定部分
  2. 边界情况处理

    • 空地址或null
    • 短地址(少于保留字符数)
    • 不同的地址格式和命名习惯
  3. 性能优化

    • 缓存常用脱敏结果
    • 预编译正则表达式
    • 避免在循环中频繁创建Pattern对象
  4. 安全性考虑

    • 确保脱敏后无法还原原始地址
    • 不同场景使用不同的脱敏强度
    • 结合业务需求调整脱敏规则

根据你的具体业务需求,可以选择合适的脱敏方案或组合使用。

抱歉,评论功能暂时关闭!