精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

加密也能模糊查?SpringBoot 玩轉(zhuǎn)敏感信息存儲(chǔ)新姿勢(shì)!

安全 數(shù)據(jù)安全
本文給出的 Spring Boot 代碼 將該方案拆解為強(qiáng)加密主存 + HMAC 分片索引兩條路徑:? 查詢時(shí)按片找索引、回表解密展示,既不暴露明文,又能實(shí)現(xiàn)接近 ???LIKE??? 的體驗(yàn)。

在金融、政務(wù)、醫(yī)療等對(duì)數(shù)據(jù)安全要求極高的行業(yè)里,“加密落盤”已經(jīng)是敏感信息(手機(jī)號(hào)、身份證號(hào)、銀行卡號(hào)等)的標(biāo)準(zhǔn)動(dòng)作。但僅存儲(chǔ)安全還不夠,真實(shí)業(yè)務(wù)里時(shí)常需要模糊檢索來(lái)提升用戶與運(yùn)營(yíng)效率,例如:輸入“6688”也能定位到某個(gè)用戶手機(jī)號(hào)。 問(wèn)題是:常規(guī)加密后,LIKE 模糊匹配天生“失效”。如果只允許精準(zhǔn)匹配,系統(tǒng)實(shí)現(xiàn)簡(jiǎn)單,但無(wú)法滿足大多數(shù)檢索訴求。于是我們需要在安全與可用之間找到“橋”。

本文在完整評(píng)估“明文匹配”“數(shù)據(jù)庫(kù)函數(shù)解密”“ES 分詞”之后,重點(diǎn)給出一套無(wú)需引入 ES、易維護(hù)、可擴(kuò)展分片存儲(chǔ)方案落地實(shí)現(xiàn),并提供可直接運(yùn)行的 Spring Boot 代碼骨架,幫你把方案真正搬到生產(chǎn)環(huán)境。

目標(biāo)

讓加密落盤的字段,也能獲得接近 LIKE 的模糊查詢體驗(yàn)

  • 數(shù)據(jù)庫(kù)存密文;
  • 查詢支持“任意位置片段”匹配;
  • 性能可控、架構(gòu)簡(jiǎn)單、易于水平擴(kuò)展。

思考路徑回顧

  1. 明文匹配(內(nèi)存解密 / 數(shù)據(jù)庫(kù)解密函數(shù)):實(shí)現(xiàn)簡(jiǎn)單,但在一致性、性能與擴(kuò)展性上有明顯短板。
  2. ES 分詞檢索:性能強(qiáng)、擴(kuò)展性好,但引入了新組件與一致性同步成本。
  3. 分片存儲(chǔ)(本文主角):把原文滾動(dòng)切片并按片加密/摘要建立反查索引,“以密取密”,保留了架構(gòu)簡(jiǎn)潔性,又兼顧性能與可運(yùn)維性。

分片存儲(chǔ)方案(核心設(shè)計(jì))

思路復(fù)述

  • 將原文(如手機(jī)號(hào) 19266889900)按固定長(zhǎng)度 k 滾動(dòng)切片:k=3 → 192, 926, 266, 668, 688, 889, 899, 990, 900
  • 整字段強(qiáng)加密密文(用于展示前解密);
  • 每個(gè)分片確定性摘要(建議 HMAC-SHA256),這樣同一明文片段總能映射為同一“密文指紋”,便于等值匹配;
  • 查詢時(shí),對(duì)關(guān)鍵詞按相同規(guī)則切片 → 計(jì)算每片 HMAC → 命中映射表 → 回表查主表 → 解密展示。

為何分片用 HMAC 而不是對(duì)稱加密? 傳統(tǒng)對(duì)稱加密(如 AES-GCM)會(huì)使用隨機(jī) IV,導(dǎo)致同樣的明文每次密文都不同,不利于等值匹配。而 HMAC(帶密鑰的哈希)穩(wěn)定、不可逆,非常適合用來(lái)做“可匹配的密文索引”。

表結(jié)構(gòu)(示例)

  • 主表 users:存放強(qiáng)加密后的敏感字段(如 phone_ciphertext
  • 索引表 data_piece_ciphertext_mapping:存放每個(gè)分片的 HMAC 指紋與業(yè)務(wù) ID 的映射

項(xiàng)目結(jié)構(gòu)

/src
 └── /main
     ├── /java
     │   └── /com
     │       └── /icoderoad
     │           └── /security
     │               ├── controller
     │               │   └── UserController.java
     │               ├── dto
     │               │   ├── UserCreateRequest.java
     │               │   └── UserView.java
     │               ├── entity
     │               │   ├── User.java
     │               │   └── DataPieceCiphertextMapping.java
     │               ├── repository
     │               │   ├── UserRepository.java
     │               │   └── DataPieceCiphertextMappingRepository.java
     │               ├── service
     │               │   ├── CryptoService.java
     │               │   ├── PieceMatchService.java
     │               │   └── UserService.java
     │               └── SecurityApplication.java
     └── /resources
         ├── application.yml
         └── schema.sql

POM 依賴(示例)

<!-- /pom.xml -->
<project>
  <properties>
    <java.version>17</java.version>
    <spring-boot.version>3.3.2</spring-boot.version>
  </properties>


  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring-boot.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>


  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>


    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>


    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <scope>runtime</scope>
    </dependency>


    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>


    <!-- 如需校驗(yàn)可加 Hibernate Validator -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
  </dependencies>


  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

配置文件

# /src/main/resources/application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/security_demo?useSSL=false&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
    username: root
    password: root
  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: true
    open-in-view: false


app:
  crypto:
    aes-key: "uE2mFq7nA1b4C7d9uE2mFq7nA1b4C7d9"   # 32字節(jié),用于AES-256-GCM(示例)
    hmac-key: "HmacKey-ChangeMe-Prod-Safe"       # HMAC-SHA256 密鑰(示例)
    piece-length: 3

生產(chǎn)環(huán)境請(qǐng)使用 KMS / 環(huán)境變量注入,不要把密鑰寫死在配置里。

建表 SQL(可直接執(zhí)行)

-- /src/main/resources/schema.sql


CREATE TABLE IF NOT EXISTS users (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(64) NOT NULL,
  phone_ciphertext VARCHAR(512) NOT NULL COMMENT '整字段強(qiáng)加密密文(含IV)',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;


CREATE TABLE IF NOT EXISTS data_piece_ciphertext_mapping (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  biz_id BIGINT NOT NULL COMMENT '指向users.id',
  piece_ciphertext CHAR(64) NOT NULL COMMENT '分片HMAC-SHA256十六進(jìn)制',
  piece_len INT NOT NULL DEFAULT 3,
  INDEX idx_piece (piece_ciphertext),
  INDEX idx_biz (biz_id),
  UNIQUE KEY uk_biz_piece (biz_id, piece_ciphertext, piece_len),
  CONSTRAINT fk_piece_user FOREIGN KEY (biz_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

核心代碼實(shí)現(xiàn)

啟動(dòng)類

// /src/main/java/com/icoderoad/security/SecurityApplication.java
package com.icoderoad.security;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class SecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication.class, args);
    }
}

實(shí)體類

// /src/main/java/com/icoderoad/security/entity/User.java
package com.icoderoad.security.entity;


import jakarta.persistence.*;
import lombok.*;


import java.time.LocalDateTime;


@Entity
@Table(name = "users")
@Getter @Setter @Builder
@NoArgsConstructor @AllArgsConstructor
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;


    @Column(nullable = false, length = 64)
    private String username;


    @Column(name = "phone_ciphertext", nullable = false, length = 512)
    private String phoneCiphertext;


    @Column(name = "created_at")
    private LocalDateTime createdAt;
}
// /src/main/java/com/icoderoad/security/entity/DataPieceCiphertextMapping.java
package com.icoderoad.security.entity;


import jakarta.persistence.*;
import lombok.*;


@Entity
@Table(name = "data_piece_ciphertext_mapping",
       uniqueConstraints = {
           @UniqueConstraint(name = "uk_biz_piece", columnNames = {"biz_id","piece_ciphertext","piece_len"})
       },
       indexes = {
           @Index(name = "idx_piece", columnList = "piece_ciphertext"),
           @Index(name = "idx_biz", columnList = "biz_id")
       })
@Getter @Setter @Builder
@NoArgsConstructor @AllArgsConstructor
public class DataPieceCiphertextMapping {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;


    @Column(name = "biz_id", nullable = false)
    private Long bizId;


    @Column(name = "piece_ciphertext", nullable = false, length = 64)
    private String pieceCiphertext;


    @Column(name = "piece_len", nullable = false)
    private Integer pieceLen;
}

Repository

// /src/main/java/com/icoderoad/security/repository/UserRepository.java
package com.icoderoad.security.repository;


import com.icoderoad.security.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;


public interface UserRepository extends JpaRepository<User, Long> {
}
// /src/main/java/com/icoderoad/security/repository/DataPieceCiphertextMappingRepository.java
package com.icoderoad.security.repository;


import com.icoderoad.security.entity.DataPieceCiphertextMapping;
import org.springframework.data.jpa.repository.JpaRepository;


import java.util.Collection;
import java.util.List;


public interface DataPieceCiphertextMappingRepository extends JpaRepository<DataPieceCiphertextMapping, Long> {


    List<DataPieceCiphertextMapping> findByPieceCiphertextInAndPieceLen(Collection<String> pieceCiphertexts, Integer pieceLen);


    List<DataPieceCiphertextMapping> findByBizId(Long bizId);


    void deleteByBizId(Long bizId);
}

DTO

// /src/main/java/com/icoderoad/security/dto/UserCreateRequest.java
package com.icoderoad.security.dto;


import jakarta.validation.constraints.NotBlank;
import lombok.Data;


@Data
public class UserCreateRequest {
    @NotBlank
    private String username;


    @NotBlank
    private String phone;   // 明文手機(jī)號(hào)
}
// /src/main/java/com/icoderoad/security/dto/UserView.java
package com.icoderoad.security.dto;


import lombok.*;


@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserView {
    private Long id;
    private String username;
    private String phone;  // 解密后的明文返回
}

加密與分片服務(wù)

// /src/main/java/com/icoderoad/security/service/CryptoService.java
package com.icoderoad.security.service;


import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;


import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;


@Service
public class CryptoService {


    @Value("${app.crypto.aes-key}")
    private String aesKeyStr;


    @Value("${app.crypto.hmac-key}")
    private String hmacKeyStr;


    private SecretKey aesKey;
    private SecretKey hmacKey;
    private final SecureRandom random = new SecureRandom();


    @PostConstruct
    public void init() {
        this.aesKey = new SecretKeySpec(aesKeyStr.getBytes(StandardCharsets.UTF_8), "AES");
        this.hmacKey = new SecretKeySpec(hmacKeyStr.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
    }


    /**
     * AES-256-GCM 加密,返回 Base64(iv || ciphertext || tag)
     */
    public String encryptField(String plaintext) {
        try {
            byte[] iv = new byte[12];
            random.nextBytes(iv);
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            GCMParameterSpec spec = new GCMParameterSpec(128, iv);
            cipher.init(Cipher.ENCRYPT_MODE, aesKey, spec);
            byte[] ct = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
            ByteBuffer buf = ByteBuffer.allocate(iv.length + ct.length);
            buf.put(iv);
            buf.put(ct);
            return Base64.getEncoder().encodeToString(buf.array());
        } catch (Exception e) {
            throw new IllegalStateException("encrypt failed", e);
        }
    }


    /**
     * AES-256-GCM 解密,輸入 Base64(iv || ciphertext || tag)
     */
    public String decryptField(String base64) {
        try {
            byte[] all = Base64.getDecoder().decode(base64);
            byte[] iv = new byte[12];
            System.arraycopy(all, 0, iv, 0, 12);
            byte[] ct = new byte[all.length - 12];
            System.arraycopy(all, 12, ct, 0, ct.length);
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(128, iv));
            return new String(cipher.doFinal(ct), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new IllegalStateException("decrypt failed", e);
        }
    }


    /**
     * HMAC-SHA256(十六進(jìn)制小寫),用于分片“確定性密文索引”
     */
    public String hmacPiece(String piece) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(hmacKey);
            byte[] raw = mac.doFinal(piece.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder(raw.length * 2);
            for (byte b : raw) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new IllegalStateException("hmac failed", e);
        }
    }
}
// /src/main/java/com/icoderoad/security/service/PieceMatchService.java
package com.icoderoad.security.service;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;


import java.util.*;


@Service
public class PieceMatchService {


    @Value("${app.crypto.piece-length}")
    private int defaultPieceLen;


    /** 對(duì)明文進(jìn)行滾動(dòng)分片(窗口大小 = pieceLen),最少返回一次(若長(zhǎng)度不足則返回原文) */
    public List<String> rollingPieces(String plaintext, Integer pieceLen) {
        int k = (pieceLen == null || pieceLen <= 0) ? defaultPieceLen : pieceLen;
        if (plaintext == null || plaintext.isEmpty()) return List.of();
        if (plaintext.length() <= k) return List.of(plaintext);


        List<String> res = new ArrayList<>();
        for (int i = 0; i + k <= plaintext.length(); i++) {
            res.add(plaintext.substring(i, i + k));
        }
        return res;
    }
}

業(yè)務(wù)服務(wù)

// /src/main/java/com/icoderoad/security/service/UserService.java
package com.icoderoad.security.service;


import com.icoderoad.security.dto.UserCreateRequest;
import com.icoderoad.security.dto.UserView;
import com.icoderoad.security.entity.DataPieceCiphertextMapping;
import com.icoderoad.security.entity.User;
import com.icoderoad.security.repository.DataPieceCiphertextMappingRepository;
import com.icoderoad.security.repository.UserRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;


import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
public class UserService {


    private final UserRepository userRepository;
    private final DataPieceCiphertextMappingRepository mappingRepository;
    private final CryptoService cryptoService;
    private final PieceMatchService pieceMatchService;


    @Value("${app.crypto.piece-length}")
    private int defaultPieceLen;


    @Transactional
    public UserView createUser(UserCreateRequest req) {
        String cipher = cryptoService.encryptField(req.getPhone());
        User user = User.builder()
                .username(req.getUsername())
                .phoneCiphertext(cipher)
                .createdAt(LocalDateTime.now())
                .build();
        user = userRepository.save(user);


        // 構(gòu)建并保存分片映射(HMAC)
        List<String> pieces = pieceMatchService.rollingPieces(req.getPhone(), defaultPieceLen);
        if (pieces.isEmpty()) {
            // 長(zhǎng)度不足片長(zhǎng):也建立一個(gè)分片
            pieces = List.of(req.getPhone());
        }
        int k = Math.min(defaultPieceLen, req.getPhone().length());


        List<DataPieceCiphertextMapping> mappings = pieces.stream()
                .map(p -> DataPieceCiphertextMapping.builder()
                        .bizId(user.getId())
                        .pieceCiphertext(cryptoService.hmacPiece(p))
                        .pieceLen(k)
                        .build())
                .toList();
        mappingRepository.saveAll(mappings);


        return UserView.builder()
                .id(user.getId())
                .username(user.getUsername())
                .phone(req.getPhone()) // 返回明文(通常應(yīng)只對(duì)有權(quán)限的端點(diǎn)返回)
                .build();
    }


    /**
     * 關(guān)鍵詞模糊查詢:對(duì)關(guān)鍵詞滾動(dòng)分片 -> HMAC -> 命中映射 -> 回表 -> 解密返回
     */
    @Transactional
    public List<UserView> searchByKeyword(String keyword, Integer pieceLen) {
        if (keyword == null || keyword.isBlank()) return List.of();
        int k = (pieceLen == null || pieceLen <= 0) ? defaultPieceLen : pieceLen;


        // 分片
        List<String> parts = pieceMatchService.rollingPieces(keyword, k);
        if (parts.isEmpty()) {
            parts = List.of(keyword);
            k = keyword.length();
        }
        // HMAC
        List<String> hmacs = parts.stream().map(cryptoService::hmacPiece).toList();


        // 命中映射
        var hits = mappingRepository.findByPieceCiphertextInAndPieceLen(hmacs, k);
        if (hits.isEmpty()) return List.of();


        // 聚合 bizId
        Set<Long> bizIds = hits.stream().map(DataPieceCiphertextMapping::getBizId).collect(Collectors.toSet());
        var users = userRepository.findAllById(bizIds);


        // 解密并返回
        return users.stream().map(u -> UserView.builder()
                .id(u.getId())
                .username(u.getUsername())
                .phone(cryptoService.decryptField(u.getPhoneCiphertext()))
                .build()).toList();
    }


    /**
     * 更新手機(jī)號(hào):重建映射(示例)
     */
    @Transactional
    public UserView updatePhone(Long userId, String newPhone) {
        var user = userRepository.findById(userId).orElseThrow();
        user.setPhoneCiphertext(cryptoService.encryptField(newPhone));
        userRepository.save(user);


        // 清理舊映射,重建新映射
        mappingRepository.deleteByBizId(userId);
        List<String> pieces = pieceMatchService.rollingPieces(newPhone, defaultPieceLen);
        if (pieces.isEmpty()) pieces = List.of(newPhone);
        int k = Math.min(defaultPieceLen, newPhone.length());


        List<DataPieceCiphertextMapping> mappings = pieces.stream()
                .map(p -> DataPieceCiphertextMapping.builder()
                        .bizId(userId)
                        .pieceCiphertext(cryptoService.hmacPiece(p))
                        .pieceLen(k)
                        .build())
                .toList();
        mappingRepository.saveAll(mappings);


        return UserView.builder()
                .id(user.getId())
                .username(user.getUsername())
                .phone(newPhone)
                .build();
    }
}

控制器

// /src/main/java/com/icoderoad/security/controller/UserController.java
package com.icoderoad.security.controller;


import com.icoderoad.security.dto.UserCreateRequest;
import com.icoderoad.security.dto.UserView;
import com.icoderoad.security.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;


import java.util.List;


@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {


    private final UserService userService;


    @PostMapping
    public UserView create(@Valid @RequestBody UserCreateRequest req) {
        return userService.createUser(req);
    }


    @GetMapping("/search")
    public List<UserView> search(@RequestParam("keyword") String keyword,
                                 @RequestParam(value = "pieceLen", required = false) Integer pieceLen) {
        return userService.searchByKeyword(keyword, pieceLen);
    }


    @PutMapping("/{id}/phone")
    public UserView updatePhone(@PathVariable("id") Long id,
                                @RequestParam("phone") String phone) {
        return userService.updatePhone(id, phone);
    }
}

接口示例(快速驗(yàn)證)

# 新增用戶
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","phone":"19266889900"}'


# 模糊搜索(默認(rèn)片長(zhǎng)=3)
curl "http://localhost:8080/api/users/search?keyword=6688"


# 指定片長(zhǎng)=2(更密集的匹配,索引體量更大)
curl "http://localhost:8080/api/users/search?keyword=2688&pieceLen=2"


# 更新手機(jī)號(hào)(自動(dòng)重建索引映射)
curl -X PUT "http://localhost:8080/api/users/1/phone?phone=13900006666"

性能與運(yùn)維建議

  • 片長(zhǎng)選擇

k 越小,命中更“敏感”,但索引量線性增大(近似 len - k + 1)。

常見(jiàn)經(jīng)驗(yàn):手機(jī)號(hào)/證件號(hào)等定長(zhǎng)字段,k=3 比較均衡。

  • 索引表擴(kuò)展

量大時(shí)優(yōu)先考慮分表表分區(qū),并對(duì) piece_ciphertext 建合適的前綴索引(本例為整值索引)。

  • 安全邊界

主表用強(qiáng)加密(AES-GCM)

分片索引用 HMAC(不可逆),即使索引泄露,也很難回推出明文(注意密鑰保護(hù))。

  • 一致性

新增/更新時(shí),同步寫主表與索引表,確保在一個(gè)事務(wù)內(nèi)完成。

  • 可觀測(cè)性

監(jiān)控映射表膨脹速度與熱點(diǎn)分片(例如“000”“123”會(huì)更常見(jiàn)),必要時(shí)做去重優(yōu)化或增加布隆過(guò)濾以減少回表次數(shù)。

總結(jié)

敏感數(shù)據(jù)加密后的模糊檢索不是一道“單選題”。

  • 小規(guī)模、單節(jié)點(diǎn):內(nèi)存明文匹配能最快上線,但擴(kuò)展性差;
  • 小表:數(shù)據(jù)庫(kù)函數(shù)解密簡(jiǎn)單易懂,但性能天花板明顯;
  • 超大規(guī)模:ES 分詞性能強(qiáng)悍,代價(jià)是運(yùn)維復(fù)雜度與一致性同步;
  • 分片存儲(chǔ)方案:在不引入新組件的前提下取得性能、成本與工程復(fù)雜度的平衡,特別適合中大型業(yè)務(wù)在自有數(shù)據(jù)庫(kù)上演進(jìn)。

本文給出的 Spring Boot 代碼 將該方案拆解為強(qiáng)加密主存 + HMAC 分片索引兩條路徑: 查詢時(shí)按片找索引、回表解密展示,既不暴露明文,又能實(shí)現(xiàn)接近 LIKE 的體驗(yàn)。你可以據(jù)此直接集成到現(xiàn)有系統(tǒng),并根據(jù)數(shù)據(jù)規(guī)模靈活調(diào)參(如 piece-length、分庫(kù)分表策略)。

責(zé)任編輯:武曉燕 來(lái)源: 路條編程
相關(guān)推薦

2025-04-28 04:22:00

Spring動(dòng)態(tài)SQL

2024-10-28 07:10:00

scroll標(biāo)記前端網(wǎng)格布局

2025-02-17 11:41:14

2024-04-30 11:49:16

瀏覽器前端開發(fā)折疊屏應(yīng)用

2010-09-02 17:31:42

VisualStudi微軟flash

2025-09-08 04:07:00

SpringApache工具

2025-02-19 12:00:00

SpringBootDeepSeekAI

2021-05-26 08:21:43

@Autowired項(xiàng)目@Resouce

2024-01-18 15:17:56

谷歌云計(jì)算三星

2018-02-25 11:24:02

APPiPhone手機(jī)

2025-02-07 10:52:00

2020-12-22 09:34:20

JavaScript開發(fā)技術(shù)

2024-06-25 12:10:26

2019-02-27 09:08:20

Java 8StringJoineIDEA

2020-07-22 08:49:43

加密

2023-07-18 09:00:00

ChatGPT文本轉(zhuǎn)語(yǔ)音

2025-04-21 03:30:00

2023-08-15 08:01:12

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美一区二区三区成人久久片| 97在线观看视频国产| 国产欧美精品一二三| 美女91在线| 欧美韩日一区二区三区四区| 亚洲影院色无极综合| 青草视频在线观看免费| 雨宫琴音一区二区三区| 精品一区二区三区三区| 国产成人av片| 国产亚洲精彩久久| 婷婷久久综合九色综合伊人色| 在线免费一区| 国产最新视频在线观看| 国产69精品久久久久777| 国产国语刺激对白av不卡| 欧美精品色哟哟| 久久在线免费| 亚洲欧美www| 色悠悠在线视频| 亚洲网站三级| 在线观看亚洲专区| 日韩免费一级视频| 色呦呦在线播放| 综合久久给合久久狠狠狠97色| 欧美日韩国产高清视频| 欧美一级淫片aaaaaa| 国产一区二区中文字幕| 国产精品日日摸夜夜添夜夜av| 精品国产免费观看| 欧美日韩亚洲一区| 日韩视频在线免费| 久久久久久成人网| 久操精品在线| 亚洲免费精彩视频| 鲁大师私人影院在线观看| 国产精品任我爽爆在线播放| 日韩欧美中文字幕公布| 日韩av自拍偷拍| 久久亚洲精品中文字幕| 欧美综合亚洲图片综合区| 成人黄色片视频| 免费亚洲电影| 日本高清无吗v一区| 国产xxxxx在线观看| 台湾佬中文娱乐网欧美电影| 五月天激情综合| 青青草成人免费在线视频| 丁香影院在线| 亚洲一区在线观看视频| 男人天堂a在线| 黑人极品ⅴideos精品欧美棵| 一区二区三区免费在线观看| 日本黄大片在线观看| 性欧美video高清bbw| 亚洲一区二区五区| 精品少妇在线视频| 天堂av中文在线观看| 欧美日韩一区二区三区| 免费日韩视频在线观看| 免费在线观看一区| 欧美日本在线观看| 波多野结衣免费观看| 奇米一区二区| 亚洲黄色www| 中文字幕一区二区人妻在线不卡| 神马电影久久| 菠萝蜜影院一区二区免费| 2021亚洲天堂| 亚洲精品一级| 国产精品草莓在线免费观看| 一级做a爱片性色毛片| 国产福利精品一区| 韩国成人动漫在线观看| 国产青青草在线| 中文字幕字幕中文在线中不卡视频| 日本xxx免费| 17videosex性欧美| 在线免费亚洲电影| 天天综合天天添夜夜添狠狠添| 97视频一区| 亚洲欧洲美洲在线综合| 国产精品久久国产精麻豆96堂| 欧美涩涩网站| 国产精品第七影院| www天堂在线| 久久精品在这里| 日本女人高潮视频| 中文在线最新版地址| 欧美精品视频www在线观看| 稀缺小u女呦精品呦| 成人一区不卡| 海角国产乱辈乱精品视频| 国产美女www| 国产成人精品www牛牛影视| 免费看污久久久| v天堂福利视频在线观看| 欧美性猛交xxxx偷拍洗澡| aaa一级黄色片| 伊人春色精品| 欧美大秀在线观看| 又污又黄的网站| 99久久伊人精品| 免费看污污视频| 嫩草伊人久久精品少妇av杨幂| 精品国产伦一区二区三区观看方式| 国产成人无码精品久久二区三| 伊人成年综合电影网| 成人激情电影一区二区| 日本人妖在线| 亚洲不卡av一区二区三区| 中文字幕免费高清在线| 精品日韩一区| 91黑丝在线观看| www.日韩高清| 成人欧美一区二区三区白人| 亚洲国产精品毛片av不卡在线| 国产成人精品亚洲线观看| 日韩中文字幕免费| www.国产一区二区| 国产精品亚洲人在线观看| 色狠狠久久av五月综合| 乡村艳史在线观看| 亚洲第一免费播放区| 成人高潮免费视频| 美女视频一区二区| 日韩精品在在线一区二区中文| 国产在线美女| 亚洲黄色片网站| www.youjizz.com亚洲| 国产精品一区二区久激情瑜伽| 亚洲日本一区二区三区在线不卡| 日韩av超清在线观看| 日韩高清不卡av| 日韩精品在线免费视频| av电影天堂一区二区在线| 国产乱淫av片杨贵妃| 91成人入口| 欧美精品精品精品精品免费| 性少妇videosexfreexxx片| 亚洲手机成人高清视频| 网站在线你懂的| 亚洲精品888| 91精品国产综合久久久久久丝袜| h片在线播放| 日韩精品一区二区三区中文不卡| 清纯粉嫩极品夜夜嗨av| 成人小视频免费在线观看| av高清在线免费观看| 久久综合五月婷婷| 欧美孕妇性xx| 97电影在线观看| 6080yy午夜一二三区久久| 一起操在线播放| 国产成a人亚洲精| 成人毛片一区二区| 婷婷精品在线| 国产精品扒开腿做爽爽爽男男 | 韩国女主播成人在线| 这里只有精品66| 天堂va在线高清一区| 午夜剧场成人观在线视频免费观看| 欧美少妇bbw| 色婷婷香蕉在线一区二区| 国产综合精品在线| 国内精品免费在线观看| www.好吊操| 中日韩免视频上线全都免费| 国产精品第七十二页| www久久日com| 亚洲精品国产精品乱码不99按摩| 无码人妻丰满熟妇区bbbbxxxx| 国产精品色哟哟网站| 四川一级毛毛片| 国产欧美在线| 在线观看亚洲视频啊啊啊啊| 亚洲精品影片| 国产精品吊钟奶在线| 成视频免费观看在线看| 日韩精品在线免费观看视频| 在线观看毛片网站| 亚洲国产精品久久一线不卡| 国产美女精品久久| 国内精品视频一区二区三区八戒| 免费看国产一级片| 欧美电影免费播放| 国产一区二区在线网站| 亚洲国产一区二区久久| 2019中文字幕在线| 成人毛片av在线| 国产视频综合在线| 国产成人麻豆精品午夜在线| 色噜噜狠狠一区二区三区果冻| 欧美黄色aaa| 久久久久久99精品| 超碰人人cao| 肉丝袜脚交视频一区二区| 免费高清一区二区三区| 成人vr资源| 九色91在线视频| 国产精品美女久久久久| 国产suv精品一区二区三区88区| www视频在线免费观看| 在线一区二区日韩| 熟妇人妻系列aⅴ无码专区友真希 熟妇人妻av无码一区二区三区 | 国产精品国三级国产av| 人人狠狠综合久久亚洲婷| 国产亚洲二区| 一区二区免费| 成人欧美一区二区三区在线湿哒哒 | 懂色av中文一区二区三区天美 | 免费观看成人高潮| 亚洲片国产一区一级在线观看| 午夜精品久久久久久久99老熟妇 | 欧美日韩另类视频| 中文字幕在线观看成人 | 二区视频在线观看| 亚洲蜜臀av乱码久久精品| 久久久久久国产免费a片| 99国产精品国产精品久久| 在线观看你懂的视频| 黑人巨大精品欧美一区| 国产 porn| 热久久国产精品| 亚洲人成色77777| 亚洲一区久久| 黄色一级视频片| 一区二区国产在线观看| 国产毛片久久久久久国产毛片| 午夜精品视频一区二区三区在线看| 色一情一乱一伦一区二区三区| 国产精品一线天粉嫩av| 蜜桃日韩视频| 精品在线99| 日本一区二区精品视频| 一区二区三区韩国免费中文网站| 欧美日韩精品中文字幕一区二区| 欧美18xxxx| 鲁鲁视频www一区二区| 秋霞影视一区二区三区| 久久国产精品亚洲va麻豆| 欧美电影在线观看完整版| 九9re精品视频在线观看re6| 久久综合五月婷婷| 免费亚洲一区二区| 亚洲97av| 五月天久久狠狠| 久久视频在线| mm131午夜| 午夜精品免费| 国产免费黄色小视频| 翔田千里一区二区| 能看的毛片网站| 麻豆精品国产传媒mv男同| 国产精品自拍视频在线| 国产毛片精品视频| 亚洲午夜久久久久久久久| 不卡一二三区首页| 亚洲午夜福利在线观看| 国产精品久久毛片| 99精品久久久久| 午夜精品123| 中文字幕亚洲乱码熟女1区2区| 欧美性极品少妇| 国产女无套免费视频| 精品久久国产老人久久综合| 日本aaa在线观看| 色哟哟入口国产精品| 91一区二区三区在线| 羞羞色国产精品| 国产福利一区二区三区在线播放| 18成人在线| 亚洲日本三级| 中国 免费 av| 一本久道综合久久精品| 尤物国产在线观看| 岛国av在线一区| 99精品欧美一区二区| 亚洲综合丝袜美腿| 日本免费精品视频| 欧美一级片在线观看| 蜜桃成人在线视频| 九九热精品视频在线播放| 波多野结衣久久精品| 成人午夜在线观看| 老司机成人在线| 特级黄色录像片| 亚洲欧美清纯在线制服| 污污的视频免费观看| 久久一区二区三区国产精品| 男人的天堂久久久| 色视频一区二区| 成人激情四射网| 日韩中文理论片| 亚洲天堂一区二区| 国产一区二区三区免费不卡| 99精品全国免费观看视频软件| a在线视频观看| 国产一区二区三区在线观看免费视频| 白丝女仆被免费网站| 亚洲国产日韩a在线播放性色| 在线免费观看一区二区| 亚洲欧美成人精品| 黄页网站大全在线免费观看| 国产在线精品成人一区二区三区| 日韩三级av| 国产一区二区三区小说| 极品少妇xxxx偷拍精品少妇| 国产精品无码久久久久久| 亚洲国产综合色| 国产精品久久777777换脸| 亚洲视频日韩精品| 爱搞国产精品| 国产精品久久国产三级国电话系列| 亚洲美女视频| 日韩在线一区视频| 欧美激情在线一区二区| 久久99国产综合精品免费| 亚洲国产精品电影| 黄页在线观看免费| 动漫一区二区在线| 你懂的网址国产 欧美| 在线观看国产福利| 国产精品天天摸av网| 国产精品乱码一区二区视频| 亚洲激情国产精品| 超碰在线最新网址| aa成人免费视频| 欧美黄在线观看| 日本wwww色| 亚洲一区二区三区中文字幕在线| 国产哺乳奶水91在线播放| 久久综合亚洲社区| 91成人app| 亚洲成人动漫在线| 国产宾馆实践打屁股91| 丰满少妇高潮久久三区| 日韩免费看网站| 爱福利在线视频| 精品乱码一区| 午夜在线视频观看日韩17c| 中文字幕av观看| 色一情一伦一子一伦一区| 国产在线视频网| 国产精品久久久久久久久影视 | 6—12呦国产精品| 色妞在线综合亚洲欧美| 四虎国产精品免费久久| 97精品国产97久久久久久粉红| 国产精品一二三四| 日韩欧美一区二区一幕| 日韩精品视频免费在线观看| 国模一区二区| 免费成人深夜夜行网站视频| 国产精品一二三| 天天爽夜夜爽夜夜爽精品| 亚洲欧美日韩区| 日本电影久久久| 日韩一级免费看| 92国产精品观看| 糖心vlog精品一区二区| 精品国产一区二区三区久久狼黑人 | 亚洲精品三区| 久久久99精品视频| 91影院在线免费观看| 伊人久久国产精品| 欧美成人亚洲成人| 综合伊思人在钱三区| 亚洲成人福利在线| 一区二区成人在线| 男女视频在线观看| 成人免费激情视频| 伊人久久婷婷| 亚洲色图第四色| 欧美tk丨vk视频| 日本精品网站| 人妻少妇精品久久| 国产亚洲精品中文字幕| 国产女人18毛片水18精| 日韩美女在线观看| 欧美在线影院| 女女互磨互喷水高潮les呻吟| 欧美一三区三区四区免费在线看 | 亚洲区欧美区| 男人天堂资源网| 欧美精品一区男女天堂| 国产精品亲子伦av一区二区三区| 日本黄大片在线观看| 国产精品无遮挡| 人妻妺妺窝人体色www聚色窝| 国产精品久久久久一区二区| 在线成人h网| 日本精品在线免费观看| 亚洲免费中文字幕| 在线日韩成人| 99九九99九九九99九他书对| 日韩欧美在线视频观看| 色婷婷在线播放| 亚洲巨乳在线观看| 91亚洲资源网|