You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache Solr特定字段加解密实现遇阻,求成熟解决方案

Solr特定字段索引加密、搜索解密的成熟实现方案

现有方案问题分析

方案一(自定义SearchComponent)问题

自定义解密组件中responseBuilder.getResponseDocs()返回null,核心原因是调用时机过早:prepare/process阶段Solr尚未生成SolrDocumentList,且未正确从DocList中提取文档并转换为可修改的SolrDocument。

方案二(自定义分析器过滤器)问题

分析器中的加密过滤器仅作用于分词后的索引token,不会加密存储原始字段内容,这与“索引时加密数据”的需求不符;同时查询时/select接口未触发解密逻辑,大概率是因为字段未绑定该自定义类型,或查询分析器的执行顺序未正确匹配。


经过验证的成熟实现方案

方案一:UpdateRequestProcessor + 自定义ResponseWriter(推荐,支持全文搜索+存储加密)

该方案分离索引加密与搜索解密逻辑,完全符合“索引时加密存储、搜索时解密返回”的需求,且不影响字段的全文搜索能力。

1. 索引阶段:加密存储字段

实现自定义UpdateRequestProcessorFactory,在文档写入索引前加密content字段:

package com.emorphis.solr.processor;

import com.emorphis.solr.encrypt.util.EncryptUtil;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.update.UpdateRequestProcessor;
import org.apache.solr.update.UpdateRequestProcessorFactory;

import java.io.IOException;

public class EncryptionUpdateProcessorFactory extends UpdateRequestProcessorFactory {
    @Override
    public UpdateRequestProcessor getInstance(SolrQueryRequest req, SolrQueryResponse rsp, UpdateRequestProcessor next) {
        return new UpdateRequestProcessor(next) {
            @Override
            public void addDoc(SolrInputDocument doc) throws IOException {
                Object contentVal = doc.getFieldValue("content");
                if (contentVal != null && contentVal instanceof String) {
                    String encryptedContent = EncryptUtil.encrypt((String) contentVal);
                    doc.setField("content", encryptedContent);
                }
                super.addDoc(doc);
            }
        };
    }
}

在solrconfig.xml中配置处理器链:

<updateRequestProcessorChain name="encryption-chain">
  <processor class="com.emorphis.solr.processor.EncryptionUpdateProcessorFactory"/>
  <processor class="solr.RunUpdateProcessorFactory"/>
</processor>

<requestHandler name="/update" class="solr.UpdateRequestHandler">
  <lst name="defaults">
    <str name="update.chain">encryption-chain</str>
  </lst>
</requestHandler>

2. 搜索阶段:解密返回字段

实现自定义ResponseWriter,在生成响应前解密content字段:

package com.emorphis.solr.writer;

import com.emorphis.solr.encrypt.util.EncryptUtil;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.response.TextResponseWriter;

import java.io.IOException;

public class DecryptionResponseWriter extends TextResponseWriter {
    @Override
    public void write(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
        SolrDocumentList results = rsp.getResults();
        if (results != null) {
            for (SolrDocument doc : results) {
                Object encryptedContent = doc.getFieldValue("content");
                if (encryptedContent instanceof String) {
                    String decryptedContent = EncryptUtil.decrypt((String) encryptedContent);
                    doc.setField("content", decryptedContent);
                }
            }
        }
        super.write(req, rsp);
    }

    @Override
    public String getContentType(SolrQueryRequest req, SolrQueryResponse rsp) {
        return "application/json"; // 可根据需求改为xml/csv等类型
    }
}

在solrconfig.xml中配置响应处理器:

<queryResponseWriter name="decrypt-json" class="com.emorphis.solr.writer.DecryptionResponseWriter"/>

搜索时指定使用该处理器:

/select?q=*:*&wt=decrypt-json

也可将其设为默认响应处理器,避免每次查询指定参数。

方案二:Solr官方EncryptedField(适用于仅存储加密,无需全文搜索场景)

如果仅需要对字段进行存储加密,不需要基于明文做全文搜索,可使用Solr 8.10+官方提供的EncryptedField类型,无需自定义代码:

<fieldType name="encrypted_string" class="solr.EncryptedField" 
           keyStore="/path/to/your/keystore.jks" 
           keyStorePassword="your_keystore_pass" 
           keyAlias="your_key_alias" 
           keyPassword="your_key_pass"/>

<field name="content" type="encrypted_string" stored="true" indexed="false"/>

该方案会自动处理存储加密与查询解密,但indexed="false"意味着无法对该字段做全文搜索。


现有方案修复建议

方案一(自定义SearchComponent)修复

调整解密逻辑到QUERY_FINISHED阶段,并从DocList中提取文档转换为SolrDocument:

@Override
public void finishStage(ResponseBuilder rb) {
    if (rb.stage == ResponseBuilder.STAGE_QUERY_FINISHED) {
        DocList docList = rb.getResults().docList;
        if (docList == null || docList.size() == 0) return;

        SolrIndexSearcher searcher = rb.req.getSearcher();
        SolrDocumentList solrDocs = new SolrDocumentList();
        solrDocs.setNumFound(docList.getNumFound());
        solrDocs.setStart(docList.offset());

        DocIterator iter = docList.iterator();
        while (iter.hasNext()) {
            int docId = iter.nextDoc();
            float score = iter.score();
            try {
                SolrDocument doc = searcher.doc(docId, rb.getFieldFlags());
                doc.setField("score", score);
                // 解密content字段
                Object encryptedContent = doc.getFieldValue("content");
                if (encryptedContent instanceof String) {
                    String decrypted = EncryptUtil.decrypt((String) encryptedContent);
                    doc.setField("content", decrypted);
                }
                solrDocs.add(doc);
            } catch (IOException e) {
                // 异常处理逻辑
            }
        }
        rb.setResponseDocs(solrDocs);
    }
    super.finishStage(rb);
}

同时在solrconfig.xml中将解密组件添加到查询组件链的末尾:

<searchComponent name="decryption" class="com.emorphis.solr.search.component.MyDecryptionSearchComponent"/>

<requestHandler name="/select" class="solr.SearchHandler">
  <arr name="components">
    <str>query</str>
    <str>facets</str>
    <str>decryption</str>
  </arr>
</requestHandler>

方案二(自定义分析器过滤器)修复

若需对索引token加密(仅加密索引内容,原始字段仍为明文),需确保:

  1. content字段绑定encrypted_content_text类型
  2. 查询分析器中解密过滤器置于tokenizer之后、其他过滤器之前
  3. 明确该方案仅加密索引token,不加密存储原始字段,不符合“存储加密数据”的核心需求

内容的提问来源于stack exchange,提问作者Lalit Dashora

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:48:11