最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中實現(xiàn)數(shù)據(jù)脫敏的六種常用方案

 更新時間:2025年09月14日 10:25:33   作者:劉大華  
在日常的開發(fā)開發(fā)工作中,我相信各位老鐵肯定遇到過這種需求手機號中間四位得用*顯示等,既要展示部分?jǐn)?shù)據(jù),又要保證敏感信息不泄露,這就是所謂的數(shù)據(jù)脫敏,所以今天給大家分享6種我在項目中常用的脫敏方案,需要的朋友可以參考下

前言

在日常的開發(fā)開發(fā)工作中,我相信各位老鐵肯定遇到過這種需求: “手機號中間四位得用*顯示”、“身份證中間八位要隱藏”、“用戶郵箱前綴脫敏”…… 例如:

  • 手機號:13812345678138****5678
  • 身份證:430101199003078888430101********8888
  • 姓名:張三四張*四
  • 郵箱號:12345678@qq.com1234****@qq.com
  • 銀行卡:62303518888524056230********2405

既要展示部分?jǐn)?shù)據(jù),又要保證敏感信息不泄露。這就是所謂的數(shù)據(jù)脫敏。

今天給大家分享6種我在項目中常用的脫敏方案,SpringBoot項目拿來即用,可以直接復(fù)制粘貼!

方案一:Hutool工具庫(懶人必備?。?/h2>

不想造輪子可以直接用現(xiàn)成的!

// Maven依賴
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.0</version>
</dependency>

// 使用示例
String phone = "13812345678";
String maskedPhone = DesensitizedUtil.mobilePhone(phone); // 138****5678

String idCard = "430101199003078888";
String maskedIdCard = DesensitizedUtil.idCardNum(idCard, 6, 4); // 430101********8888

String name = "張三四";
String maskedName = DesensitizedUtil.chineseName(name); // 張*四

String email = "12345678@qq.com";
String maskedEmail = DesensitizedUtil.email(email); // 1234****@qq.com

String bankCard = "6230351888852405";
String maskedBankCard = DesensitizedUtil.bankCard(bankCard); // 6230********2405

功能全面,開箱即用

方案二:正則工具類

寫個工具類,需要的時候手動處理一下,這是最簡單直接的方式。

/**
 * 脫敏工具類 - 簡單直接
 */
public class SensitiveUtil {
    
    /**
     * 手機號脫敏:13812345678 -> 138****5678
     */
    public static String maskPhone(String phone) {
        if (StringUtils.isEmpty(phone) || phone.length() != 11) {
            return phone;
        }
        return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }
    
    /**
     * 身份證脫敏:430101199003078888 -> 430101********8888
     */
    public static String maskIdCard(String idCard) {
        if (StringUtils.isEmpty(idCard) || idCard.length() < 15) {
            return idCard;
        }
        return idCard.replaceAll("(\\d{6})\\d{8}(\\w{4})", "$1********$2");
    }
    
    /**
     * 姓名脫敏:張三四 -> 張*四
     */
    public static String maskName(String name) {
        if (StringUtils.isEmpty(name)) {
            return name;
        }
        if (name.length() == 1) {
            return "*";
        }
        if (name.length() == 2) {
            return name.charAt(0) + "*";
        }
        return name.substring(0, 1) + "*" + name.substring(name.length() - 1);
    }
    
    /**
     * 郵箱脫敏:12345678@qq.com -> 1234****@qq.com
     */
    public static String maskEmail(String email) {
        if (StringUtils.isEmpty(email) || !email.contains("@")) {
            return email;
        }
        String[] parts = email.split("@");
        if (parts[0].length() <= 4) {
            return parts[0].substring(0, 1) + "****@" + parts[1];
        }
        return parts[0].substring(0, 4) + "****@" + parts[1];
    }
    
    /**
     * 銀行卡脫敏:6230351888852405 -> 6230********2405
     */
    public static String maskBankCard(String bankCard) {
        if (StringUtils.isEmpty(bankCard) || bankCard.length() < 8) {
            return bankCard;
        }
        return bankCard.replaceAll("(\\d{4})\\d{8}(\\d{4})", "$1********$2");
    }
}

怎么用?很簡單:

User user = userService.getById(123);

// 手動脫敏
user.setPhone(SensitiveUtil.maskPhone(user.getPhone()));
user.setIdCard(SensitiveUtil.maskIdCard(user.getIdCard()));
user.setName(SensitiveUtil.maskName(user.getName()));
user.setEmail(SensitiveUtil.maskEmail(user.getEmail()));
user.setBankCard(SensitiveUtil.maskBankCard(user.getBankCard()));

return user;

非常簡單易懂,數(shù)據(jù)脫敏不再是問題。

但有些朋友又說了:“每寫一個接口或者每一個字段都要單獨調(diào)用,有點心累。有沒有更方便的方案?” 請看方案二。

方案三:自定義注解 + Jackson

利用Jackson的序列化機制,在返回JSON時自動對標(biāo)注了注解的字段進(jìn)行脫敏。

// 脫敏注解
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerializer.class)
public @interface Sensitive {
    SensitiveType value();
}

// 脫敏類型枚舉
public enum SensitiveType {
    PHONE,      // 手機號
    ID_CARD,    // 身份證
    NAME,       // 姓名
    EMAIL,      // 郵箱
    BANK_CARD   // 銀行卡
}

// 脫敏序列化器
public class SensitiveSerializer extends JsonSerializer<String> {
    private SensitiveType type;
    
    public SensitiveSerializer(SensitiveType type) {
        this.type = type;
    }
    
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        String result;
        switch (type) {
            case PHONE:
                result = SensitiveUtil.maskPhone(value);
                break;
            case ID_CARD:
                result = SensitiveUtil.maskIdCard(value);
                break;
            case NAME:
                result = SensitiveUtil.maskName(value);
                break;
            case EMAIL:
                result = SensitiveUtil.maskEmail(value);
                break;
            case BANK_CARD:
                result = SensitiveUtil.maskBankCard(value);
                break;
            default:
                result = value;
        }
        gen.writeString(result);
    }
}

使用方式

public class User {
    private String name;
    
    @Sensitive(SensitiveType.PHONE)
    private String phone;
    
    @Sensitive(SensitiveType.ID_CARD)
    private String idCard;
    
    @Sensitive(SensitiveType.NAME)
    private String name;
    
    @Sensitive(SensitiveType.EMAIL)
    private String email;
    
    @Sensitive(SensitiveType.BANK_CARD)
    private String bankCard;
}

性能好,只在序列化時生效。配置一次,全局都可以用,不影響業(yè)務(wù)邏輯。缺點是需要配置Jackson

方案四:Lombok + 自定義Getter(輕量級替代方案)

如果你不想引入太多框架,但用了Lombok,這個方案很合適。

思路:讓Lombok不生成默認(rèn)getter,我們自己寫一個帶脫敏邏輯的getter

1. 關(guān)閉Lombok的默認(rèn)getter

去掉@Data,只保留你需要的Lombok注解,手動添加帶脫敏的getter

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Setter
public class UserVO {

    private String name;
    private String phone;
    private String idCard;
    private String email;
    private String bankCard;

    // 自定義脫敏 getter
    public String getName() {
        return SensitiveUtil.maskName(name);
    }

    public String getPhone() {
        return SensitiveUtil.maskPhone(phone);
    }

    public String getIdCard() {
        return SensitiveUtil.maskIdCard(idCard);
    }
    
    public String getEmail() {
        return SensitiveUtil.maskEmail(email);
    }
    
    public String getBankCard() {
        return SensitiveUtil.maskBankCard(bankCard);
    }
}

這樣只要調(diào)用user.getxx(),返回的就是脫敏后的數(shù)據(jù)。

實現(xiàn)簡單,不依賴額外組件,適合小項目。但每個字段都要手動寫getter,有點啰嗦了。

方案五:AOP切面深度脫敏(推薦)

這個是主推的方案,特別適合嵌套對象、ListMap等類型。 為什么?因為前面的方法大多只處理單層DTO。
但現(xiàn)實中經(jīng)常是:

public class User {
    @Sensitive(SensitiveType.PHONE)
    private String phone; // 這個能脫敏
    
    private UserDetail detail; // 這里面還有敏感字段
}

public class UserDetail {
    @Sensitive(SensitiveType.ID_CARD)
    private String idCard; // 這個脫敏不了!
}

返回User對象時,phone字段能脫敏,但detail.idCard還是明文顯示!

解決方案:用AOP + 深度遞歸反射。詳細(xì)步驟如下:

1. 定義脫敏類型枚舉

/**
 * 脫敏類型枚舉
 */
public enum SensitiveType {
    PHONE,      // 手機號
    ID_CARD,    // 身份證
    NAME,       // 姓名
    EMAIL,      // 郵箱
    BANK_CARD   // 銀行卡
}

2. 定義注解@Sensitive

/**
 * 脫敏注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {
    SensitiveType value();
}

3. 需要保留方案一的工具類,增加以下的maskByType方法

/**
 * 根據(jù)類型脫敏
*/
public static String maskByType(String value, SensitiveType type) {
	if (StringUtils.isBlank(value)) {
            return value;
        }

        switch (type) {
            case PHONE: return maskPhone(value);
            case ID_CARD: return maskIdCard(value);
            case NAME: return maskName(value);
            case EMAIL: return maskEmail(value);
            case BANK_CARD: return maskBankCard(value);
            default: return value;
        }
    }
}

4. 寫AOP切面(核心邏輯)

/**
 * 深度脫敏AOP處理器
 */
@Aspect
@Component
@Slf4j
public class DeepSensitiveAspect {
    
    // 定義切點:攔截Controller層所有方法
    @Pointcut("execution(* com.example.controller..*.*(..))")
    public void controllerPointcut() {}
    
    // 定義切點:攔截Service層所有方法
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void servicePointcut() {}
    
    /**
     * 環(huán)繞通知:處理Controller層返回結(jié)果
     */
    @Around("controllerPointcut()")
    public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        return processDeepSensitive(result);
    }
    
    /**
     * 環(huán)繞通知:處理Service層返回結(jié)果
     */
    @Around("servicePointcut()")
    public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        return processDeepSensitive(result);
    }
    
    /**
     * 深度脫敏處理
     */
    private Object processDeepSensitive(Object obj) {
        if (obj == null) {
            return null;
        }
        
        // 處理集合類型
        if (obj instanceof List) {
            return processList((List<?>) obj);
        }
        
        // 處理數(shù)組類型
        if (obj.getClass().isArray()) {
            return processArray((Object[]) obj);
        }
        
        // 處理Map類型
        if (obj instanceof Map) {
            return processMap((Map<?, ?>) obj);
        }
        
        // 處理分頁對象(Spring Data Page)
        if (obj instanceof Page) {
            return processPage((Page<?>) obj);
        }
        
        // 處理普通Java對象
        if (isCustomClass(obj.getClass())) {
            return processObject(obj);
        }
        
        // 基本類型直接返回
        return obj;
    }
    
    /**
     * 處理List集合
     */
    private List<?> processList(List<?> list) {
        if (list == null || list.isEmpty()) {
            return list;
        }
        
        return list.stream()
            .map(this::processDeepSensitive)
            .collect(Collectors.toList());
    }
    
    /**
     * 處理數(shù)組
     */
    private Object[] processArray(Object[] array) {
        if (array == null || array.length == 0) {
            return array;
        }
        
        Object[] result = new Object[array.length];
        for (int i = 0; i < array.length; i++) {
            result[i] = processDeepSensitive(array[i]);
        }
        return result;
    }
    
    /**
     * 處理Map
     */
    private Map<?, ?> processMap(Map<?, ?> map) {
        if (map == null || map.isEmpty()) {
            return map;
        }
        
        Map<Object, Object> result = new HashMap<>();
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            result.put(entry.getKey(), processDeepSensitive(entry.getValue()));
        }
        return result;
    }
    
    /**
     * 處理分頁對象
     */
    private Page<?> processPage(Page<?> page) {
        if (page == null) {
            return null;
        }
        
        List<?> content = processList(page.getContent());
        return new PageImpl<>(content, page.getPageable(), page.getTotalElements());
    }
    
    /**
     * 處理單個對象
     */
    private Object processObject(Object obj) {
        if (obj == null) {
            return null;
        }
        
        Class<?> clazz = obj.getClass();
        try {
            // 獲取所有字段(包括父類)
            List<Field> fields = getAllFields(clazz);
            
            for (Field field : fields) {
                field.setAccessible(true);
                
                // 檢查是否有脫敏注解
                Sensitive sensitive = field.getAnnotation(Sensitive.class);
                if (sensitive != null && field.getType() == String.class) {
                    // 處理敏感字段
                    processSensitiveField(obj, field, sensitive);
                } else {
                    // 遞歸處理嵌套對象
                    processNestedField(obj, field);
                }
            }
        } catch (Exception e) {
            log.warn("脫敏處理失敗: {}", e.getMessage());
        }
        
        return obj;
    }
    
    /**
     * 處理敏感字段
     */
    private void processSensitiveField(Object obj, Field field, Sensitive sensitive) {
        try {
            String value = (String) field.get(obj);
            if (StringUtils.isNotBlank(value)) {
                String maskedValue = SensitiveUtil.maskByType(value, sensitive.value());
                field.set(obj, maskedValue);
            }
        } catch (IllegalAccessException e) {
            log.warn("字段脫敏失敗: {}", field.getName());
        }
    }
    
    /**
     * 處理嵌套字段
     */
    private void processNestedField(Object obj, Field field) {
        try {
            Object fieldValue = field.get(obj);
            if (fieldValue != null && isCustomClass(field.getType())) {
                // 遞歸處理嵌套對象
                processObject(fieldValue);
            }
        } catch (IllegalAccessException e) {
            // 忽略無法訪問的字段
        }
    }
    
    /**
     * 獲取所有字段(包括父類)
     */
    private List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null && clazz != Object.class) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }
    
    /**
     * 判斷是否為自定義類(非JDK類)
     */
    private boolean isCustomClass(Class<?> clazz) {
        return clazz != null && 
               !clazz.isPrimitive() && 
               !clazz.getName().startsWith("java.") &&
               !clazz.getName().startsWith("javax.");
    }
}

5. 實體類使用示例

/**
 * 用戶實體
 */
@Data
public class User {
    private Long id;
    
    @Sensitive(SensitiveType.NAME)
    private String name;
    
    @Sensitive(SensitiveType.PHONE)
    private String phone;
    
    @Sensitive(SensitiveType.ID_CARD)
    private String idCard;
    
    @Sensitive(SensitiveType.EMAIL)
    private String email;
    
    @Sensitive(SensitiveType.BANK_CARD)
    private String bankCard;
    
    // 嵌套對象也會被處理
    private UserDetail detail;
}

/**
 * 用戶詳情實體
 */
@Data
public class UserDetail {
    @Sensitive(SensitiveType.PHONE)
    private String emergencyPhone;
    
    @Sensitive(SensitiveType.ID_CARD)
    private String spouseIdCard;
}

6. Controller使用示例

@RestController
@RequestMapping("/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    /**
     * 返回單個用戶(自動脫敏)
     */
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    /**
     * 返回用戶列表(自動脫敏)
     */
    @GetMapping
    public List<User> getUsers() {
        return userService.getAllUsers();
    }
    
    /**
     * 返回分頁數(shù)據(jù)(自動脫敏)
     */
    @GetMapping("/page")
    public Page<User> getUsersByPage(Pageable pageable) {
        return userService.getUsersByPage(pageable);
    }
}

7. 效果驗證

{
  "id": 1,
  "name": "張*四",
  "phone": "138****5678",
  "idCard": "430101********8888",
  "email": "1234****@qq.com",
  "bankCard": "6230********2405",
  "detail": {
    "emergencyPhone": "139****8765",
    "spouseIdCard": "110105********1234"
  }
}

優(yōu)點

  • 全面支持:手機號、身份證、姓名、郵箱、銀行卡全搞定
  • 深度處理:支持多層嵌套對象脫敏
  • 零侵入:業(yè)務(wù)代碼無需任何修改
  • 高性能:使用反射緩存,性能優(yōu)化
  • 易擴展:新增脫敏類型只需擴展枚舉和工具方法

方案六:Mysql數(shù)據(jù)庫層脫敏

最簡單的MySQL脫敏SQL

SELECT 
    id,
    CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) AS phone,
    CONCAT(LEFT(id_card, 6), '********', RIGHT(id_card, 4)) AS id_card,
    CONCAT(LEFT(name, 1), '*', RIGHT(name, 1)) AS name,
    CONCAT(LEFT(email, 4), '****', SUBSTRING(email, LOCATE('@', email))) AS email,
    CONCAT(LEFT(bank_card, 4), '********', RIGHT(bank_card, 4)) AS bank_card
FROM users;

分字段詳細(xì)寫法

1. 手機號脫敏

SELECT CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) AS phone FROM users;
-- 13812345678 → 138****5678

2. 身份證脫敏

SELECT CONCAT(LEFT(id_card, 6), '********', RIGHT(id_card, 4)) AS id_card FROM users;
-- 430101199003078888 → 430101********8888

3. 姓名脫敏

SELECT 
    CASE 
        WHEN LENGTH(name) = 1 THEN '*'
        WHEN LENGTH(name) = 2 THEN CONCAT(LEFT(name, 1), '*')
        ELSE CONCAT(LEFT(name, 1), '*', RIGHT(name, 1))
    END AS name 
FROM users;
-- 張三四 → 張*四

4. 郵箱脫敏

SELECT 
    CONCAT(LEFT(email, 4), '****', SUBSTRING(email, LOCATE('@', email))) AS email 
FROM users;
-- 12345678@qq.com → 1234****@qq.com

5. 銀行卡脫敏

SELECT CONCAT(LEFT(bank_card, 4), '********', RIGHT(bank_card, 4)) AS bank_card FROM users;
-- 6230351888852405 → 6230********2405

6. 完整查詢示例

-- 查詢單個用戶
SELECT 
    id,
    CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) AS phone,
    CONCAT(LEFT(id_card, 6), '********', RIGHT(id_card, 4)) AS id_card,
    CASE 
        WHEN LENGTH(name) = 1 THEN '*'
        WHEN LENGTH(name) = 2 THEN CONCAT(LEFT(name, 1), '*')
        ELSE CONCAT(LEFT(name, 1), '*', RIGHT(name, 1))
    END AS name,
    CONCAT(LEFT(email, 4), '****', SUBSTRING(email, LOCATE('@', email))) AS email,
    CONCAT(LEFT(bank_card, 4), '********', RIGHT(bank_card, 4)) AS bank_card
FROM users 
WHERE id = 1;

-- 查詢用戶列表
SELECT 
    id,
    CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) AS phone,
    CONCAT(LEFT(name, 1), '*', RIGHT(name, 1)) AS name
FROM users 
ORDER BY id DESC 
LIMIT 10;

7. 創(chuàng)建視圖方案

如果經(jīng)常要用,建個視圖更方便:

CREATE VIEW v_user_masked AS
SELECT 
    id,
    CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) AS phone,
    CONCAT(LEFT(id_card, 6), '********', RIGHT(id_card, 4)) AS id_card,
    CASE 
        WHEN LENGTH(name) = 1 THEN '*'
        WHEN LENGTH(name) = 2 THEN CONCAT(LEFT(name, 1), '*')
        ELSE CONCAT(LEFT(name, 1), '*', RIGHT(name, 1))
    END AS name,
    CONCAT(LEFT(email, 4), '****', SUBSTRING(email, LOCATE('@', email))) AS email,
    CONCAT(LEFT(bank_card, 4), '********', RIGHT(bank_card, 4)) AS bank_card
FROM users;

-- 直接用視圖查詢
SELECT * FROM v_user_masked WHERE id = 1;

8. 聯(lián)表查詢脫敏

SELECT 
    u.id,
    CONCAT(LEFT(u.name, 1), '*', RIGHT(u.name, 1)) AS user_name,
    CONCAT(LEFT(u.phone, 3), '****', RIGHT(u.phone, 4)) AS user_phone,
    o.order_no,
    o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;

最后總結(jié)

方案優(yōu)點缺點適用場景
Hutool 工具庫開箱即用,代碼簡潔,功能全依賴第三方庫,靈活性有限快速開發(fā)、小項目、原型階段
正則工具類 + 手動調(diào)用簡單直接,無額外依賴需手動調(diào)用,侵入業(yè)務(wù)代碼單字段少量脫敏,輕量級應(yīng)用
自定義注解 + Jackson 序列化自動化序列化脫敏,性能好僅作用于 JSON 輸出,不支持嵌套對象深層脫敏REST API 返回數(shù)據(jù)脫敏
Lombok + 自定義 Getter輕量,無需切面或框架每個字段都要寫 getter,重復(fù)代碼多VO/DTO 層控制輸出,適合簡單結(jié)構(gòu)
AOP 切面深度脫敏(推薦)支持嵌套對象、集合、分頁;零侵入業(yè)務(wù);可全局生效;易擴展實現(xiàn)復(fù)雜,需理解反射和 AOP中大型項目,復(fù)雜數(shù)據(jù)結(jié)構(gòu),統(tǒng)一脫敏治理
MySQL 數(shù)據(jù)庫層脫敏不依賴 Java 層,查詢即脫敏,安全隔離SQL 復(fù)雜,維護(hù)成本高,無法動態(tài)控制報表查詢、只讀視圖、DBA 視角脫敏

最佳實踐建議

1.如果你是新手或者小項目起步,方案一(Hutool)方案二(工具類)。

2.如果你做的是標(biāo)準(zhǔn)后端服務(wù)API,推薦方案三(Jackson 注解序列化) 結(jié)合 @Sensitive注解,在返回 JSON時自動脫敏。

3.如果你的項目結(jié)構(gòu)復(fù)雜、嵌套深、List/Map/分頁多,強烈推薦 方案五(AOP深度脫敏)!這是真正意義上的“一次配置,處處脫敏”。

4.如果你有DB權(quán)限且需要給前端/報表提供固定脫敏視圖,使用 方案六(MySQL脫敏+視圖),實現(xiàn)數(shù)據(jù)訪問層面的安全隔離。

以上就是SpringBoot中實現(xiàn)數(shù)據(jù)脫敏的六種常用方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot數(shù)據(jù)脫敏方案的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

改则县| 阳朔县| 三门县| 象山县| 北流市| 嫩江县| 安岳县| 江口县| 巴林右旗| 邵武市| 城固县| 武川县| 江山市| 同德县| 漳州市| 万安县| 花莲市| 临江市| 靖远县| 长岛县| 东乌珠穆沁旗| 北辰区| 双流县| 邳州市| 乐业县| 达尔| 玉林市| 双峰县| 大化| 如皋市| 马鞍山市| 乌苏市| 彰化市| 乌审旗| 延寿县| 松江区| 东丰县| 娄烦县| 犍为县| 观塘区| 枞阳县|