精品欧美一区二区三区在线观看 _久久久久国色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)

欧美在线观看一区二区三区| 日韩欧美一区二区视频| 香蕉久久夜色| 国产乱淫a∨片免费视频| 欧美一区国产在线| 亚洲级视频在线观看免费1级| 男人天堂999| 免费超碰在线| 91免费看片在线观看| 日本精品视频在线| 黄色一级片中国| 亚洲欧美tv| 777午夜精品视频在线播放| 久青草视频在线播放| 岛国视频免费在线观看| 国产精品影视在线| 日韩av手机在线观看| 亚洲av鲁丝一区二区三区| 亚洲va久久| 日韩视频在线你懂得| 不要播放器的av网站| 日韩精品卡一| 中文字幕制服丝袜一区二区三区| 国产一区国产精品| 国产欧美日韩综合精品一区二区三区 | 99tv成人| 精品亚洲一区二区三区四区五区 | 狠狠人妻久久久久久综合| 四季av一区二区凹凸精品| 日韩精品小视频| 男人的天堂免费| 亚洲精品第一| 色网综合在线观看| 国产综合中文字幕| 欧美亚洲系列| 亚洲欧美国产77777| 日本高清视频一区二区三区| 天堂中文在线视频| 不卡影院免费观看| 成人欧美一区二区三区视频| 国产精品无码在线播放| 秋霞午夜av一区二区三区| 欧美一二三视频| 日本少妇性生活| 欧美精品日韩| 久久91亚洲精品中文字幕奶水| 亚洲一级片在线播放| 国产麻豆精品久久| 亚洲精品中文字| 久久丫精品国产亚洲av不卡| 久久夜色电影| 日韩成人av在线播放| 国产精品成人无码专区| 粉嫩一区二区三区四区公司1| 日韩欧美久久一区| 免费人成视频在线播放| 高清一区二区三区av| 91精品国产综合久久精品app| 欧美成人乱码一二三四区免费| 99久久伊人| 欧美日韩在线播放三区| 一区二区三区免费播放| 国产福利一区二区三区在线播放| 欧美日韩中文一区| av在线网址导航| 国模大尺度视频一区二区| 欧美丰满少妇xxxbbb| 成人综合久久网| 韩国一区二区三区视频| 精品国产一区二区三区四区四| 中文字幕在线国产| 欧美色资源站| 亚洲人精选亚洲人成在线| 一级片久久久久| 天天做天天爱天天综合网2021| 欧美乱大交xxxxx另类电影| 久久伊人成人网| 亚洲影音先锋| 国产精品爽爽ⅴa在线观看| 国产精品久久久久久免费| 国产a区久久久| 久久久久久亚洲精品不卡4k岛国| 久草在线网址| 综合分类小说区另类春色亚洲小说欧美| 国产盗摄视频在线观看| 精品极品在线| 欧美日韩成人综合在线一区二区| 欧美日韩一区二区区| 日韩成人午夜| 这里只有精品久久| 国内偷拍精品视频| 免费日韩av| 92看片淫黄大片看国产片| 开心激情综合网| 国产精品色一区二区三区| www婷婷av久久久影片| 一级毛片久久久| 欧美精品丝袜久久久中文字幕| www.四虎在线| 日韩大片在线播放| 国内精品久久影院| 中文字幕欧美人妻精品| 成人精品免费网站| 一区二区三区视频在线播放| 久草免费在线色站| 欧美日免费三级在线| 久久久久久婷婷| 日韩欧美一区二区三区在线视频| 久久久这里只有精品视频| 中文字幕你懂的| 91女神在线视频| 在线观看污视频| 91精品店在线| 精品伊人久久97| 免费人成年激情视频在线观看| 久久精品成人| 国产精品免费一区二区三区四区 | 麻豆传媒在线看| 欧美日韩老妇| 欧美与黑人午夜性猛交久久久| 国产口爆吞精一区二区| 久久精子c满五个校花| 日韩精品在线观看av| 国产一区 二区| 一区二区欧美亚洲| 天天干天天干天天干天天| 成人精品免费网站| 日韩一区二区高清视频| 亚洲精品自拍| 色av吧综合网| 超碰在线免费97| 91丝袜高跟美女视频| 欧美高清中文字幕| 久久久精品区| yellow中文字幕久久| 久久国产香蕉视频| 久久精品亚洲国产奇米99| 黄色免费福利视频| 美女一区二区在线观看| 久久男人资源视频| 欧美一级淫片免费视频魅影视频| 亚洲欧美成人一区二区三区| 日本中文字幕观看| 99精品视频在线| 国产精品日韩在线一区| www.亚洲免费| 欧美日韩午夜在线视频| 超碰97av在线| 欧美bbbbb| 亚洲精品视频一二三| 欧美日韩尤物久久| 在线午夜精品自拍| 亚洲视频在线观看免费视频| 国产精品久久久久影院色老大| 男操女免费网站| 欧美国产美女| 91久久嫩草影院一区二区| 黄网站视频在线观看| 91精品一区二区三区久久久久久| 国产麻豆a毛片| 国产乱子伦视频一区二区三区| 成年人视频大全| 国产成人精品亚洲线观看| 午夜精品三级视频福利| 亚州视频一区二区三区| 日韩欧美在线视频| 我想看黄色大片| 国产在线播精品第三| 国产成人一区二区三区别| 久久黄色影视| 国产国语videosex另类| 网友自拍视频在线| 日韩美女一区二区三区四区| 圆产精品久久久久久久久久久| 2019国产精品| 17c国产在线| 国产精品99免费看| 欧美伦理一区二区| 农村妇女一区二区| 欧美大片在线看| 五月婷婷开心中文字幕| 欧美最猛黑人xxxxx猛交| 日韩成人毛片视频| 99久久精品国产导航| 无码少妇一区二区三区芒果| 91成人观看| 国精产品一区二区| 欧美天堂一区二区| 久久久久久com| 大片免费播放在线视频| 欧美一区二区在线看| av黄色在线看| 日韩毛片精品高清免费| 大又大又粗又硬又爽少妇毛片 | 国产欧美高清| 日本在线观看一区| 日韩成人精品| 国产精品国产三级国产专播精品人| 九色porny在线| 亚洲精品小视频| 精品人妻一区二区三区蜜桃| 黑人巨大精品欧美一区二区免费| 国产又粗又猛又爽又黄的视频四季| 国产suv精品一区二区6| 日本人视频jizz页码69| 1024日韩| 最近免费观看高清韩国日本大全| 亚州国产精品| 99久久久精品免费观看国产| 国产精品毛片久久久久久久久久99999999| 蜜臀久久99精品久久久久久宅男| 神马久久高清| 亚洲成年人在线| 99久久久国产精品无码免费| 欧美无砖砖区免费| 久久久久久久久久免费视频| 自拍偷拍国产精品| 色综合99久久久无码国产精品| 成人一区二区三区视频在线观看| 一级做a免费视频| 久久蜜桃资源一区二区老牛| 黄色一级片在线看| 欧美私人啪啪vps| 久久最新免费视频| 欧美a级片视频| 日本在线观看一区二区三区| 中文字幕中文字幕精品| 久99久视频| 精品国内亚洲2022精品成人| 成人av资源| 伊人久久影院| 99在线高清视频在线播放| 青娱乐极品盛宴一区二区| 国产精品第一第二| 欧美xxxx做受欧美护士| 欧美一级片在线播放| a级片在线免费观看| 久久频这里精品99香蕉| 羞羞视频在线观看免费| 久久天天躁日日躁| 美女黄视频在线观看| 久久久成人精品视频| 国产视频一区二区| 久久这里有精品视频| 麻豆网站在线观看| 中文字幕日韩av电影| 亚洲成a人v欧美综合天堂麻豆| 亚洲性夜色噜噜噜7777| 国产三级视频在线| 这里只有精品久久| 男人资源在线播放| 久久不射电影网| 宅男在线观看免费高清网站| 九九热这里只有精品6| 牛牛电影国产一区二区| 性色av一区二区咪爱| 色戒汤唯在线观看| 日本久久久久久久久| 日韩制服诱惑| 国产日韩在线观看av| 成人精品视频在线观看| 98国产高清一区| 九色丨蝌蚪丨成人| 青娱乐国产91| 999国产精品| 黄色三级中文字幕| 亚洲日本视频| 国产精品igao| 国产精品一区二区你懂的| 丰满人妻一区二区三区大胸 | 亚洲国产日韩一区无码精品久久久| 国产色综合久久| 91精品少妇一区二区三区蜜桃臀| 一区二区视频免费在线观看| 日本天堂网在线观看| 欧美三级xxx| 亚洲图片视频小说| 日韩精品一区二区三区视频播放 | 精品国产一级| 狠狠色伊人亚洲综合网站色| 精品一区二区三| 91成人在线视频观看| 亚洲电影av| 亚洲这里只有精品| 国产福利一区二区三区在线视频| 精品一区二区视频在线观看| 欧美极品少妇xxxxⅹ高跟鞋| 岛国毛片在线观看| 色综合久久88色综合天天6| 一级黄色片在线看| 亚洲高清av在线| av在线播放网| 欧美大学生性色视频| 久久毛片亚洲| 亚洲一区二区三区成人在线视频精品 | 国产亚洲情侣一区二区无| 精品国产一区二区三区av片| www.99riav| 蜜桃av噜噜一区| 亚洲av成人无码一二三在线观看| 中文字幕免费不卡| 日韩少妇裸体做爰视频| 欧美嫩在线观看| 日本v片在线免费观看| 欧美成人精品影院| 国产精品久久久久久吹潮| 国产原创精品| 欧美国产精品| 午夜激情在线观看视频| 波多野结衣中文字幕一区| 三上悠亚在线观看视频| 色婷婷综合久久久久中文一区二区 | 日韩av影视在线| 在线播放免费av| 国产在线观看精品一区二区三区| 天堂网av成人| 国产精品专区在线| 成人性生交大合| 男女性高潮免费网站| 欧美午夜精品理论片a级按摩| 香蕉久久一区二区三区| 久久91亚洲精品中文字幕| 亚洲成人1区| 日日夜夜精品网站| 欧美专区在线| 成人免费毛片日本片视频| 亚洲午夜国产一区99re久久| 国产伦精品一区二区三区视频痴汉| 一本色道久久88亚洲综合88| 亚洲精品日产| 精品福利影视| 日韩图片一区| 大乳护士喂奶hd| 亚洲成人综合在线| 色婷婷av一区二区三区之红樱桃| 欧美日韩aaaa| 成人在线视频中文字幕| 国产精品三级一区二区| 国产精品系列在线观看| 一区二区成人免费视频| 69堂精品视频| 国产福利视频在线观看| 91日本在线视频| 久久精品久久久| 奇米777在线视频| 亚洲欧美aⅴ...| 亚洲产国偷v产偷v自拍涩爱| 欧美成人在线影院| 亚洲成av人片在线观看www| 黄色网络在线观看| 国产乱码精品一区二区三区av| 在线观看美女av| 日韩精品一区在线观看| segui88久久综合9999| 精品一区国产| 久久久久久久欧美精品| 日韩中文字幕有码| 欧美久久一二三四区| 顶级网黄在线播放| 成人区精品一区二区| 一区二区三区导航| 老司机福利av| 欧美亚洲动漫制服丝袜| 天天在线视频色| 亚洲在线免费看| 亚洲国产免费看| 高潮毛片无遮挡| 欧美日韩精品欧美日韩精品| 成人日批视频| 极品尤物一区二区三区| 日韩国产一区二| 日韩精品123区| 亚洲国产精品免费| 欧美在线va视频| 无码人妻精品一区二区三区99v| 国产精品18久久久久久久久久久久| 中文字幕一区二区三区手机版 | 日韩中文欧美在线| 欧美xxxooo| 亚洲精品短视频| 国产精品亚洲成在人线| 欧美成人精品免费| 国产三区在线成人av| 国产视频一二三四区| 91成人在线视频| 久久精品亚洲人成影院| 国产肉体xxxx裸体784大胆| 欧美日韩国产123区| 99热99re6国产在线播放| 香蕉久久夜色| 99在线精品观看| 亚洲一区中文字幕在线| 98精品国产自产在线观看| 亚洲91视频| 3d动漫精品啪啪一区二区下载| 91精品国产一区二区三区蜜臀| 小h片在线观看| 黄色三级中文字幕| 中文字幕一区在线| 久久经典视频|