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

Java中检测Office文档(.docx/.doc等)是否受密码保护

Office文档(.docx/.doc/.ppt/.pptx/.xls/.xlsx)密码保护检测方案

问题描述

我有一个Java应用,需要检测上传的.docx、.doc、.ppt、.pptx、.xls、.xlsx格式文件是否受密码保护。目前已经通过Apache PDFBox实现了PDF文件的加密检测,代码如下:

private boolean isPdfPasswordProtected(InputStream inputStream) {
    try (PDDocument document = PDDocument.load(inputStream)) {
        return document.isEncrypted();
    } catch (InvalidPasswordException e) {
        return true;
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}

但针对上述Office格式文件,尝试使用Apache POI-ooxml及Apache Tika元数据检测的代码均无法生效,请求提供可行的实现方案。

尝试过的Apache POI代码

private boolean isWordPasswordProtected(InputStream inputStream, String contentType) {
    try {
        if (contentType.equalsIgnoreCase("application/msword")) { // Check for DOC files first
            try (POIFSFileSystem poifs = new POIFSFileSystem(inputStream)) {
                HWPFDocument doc = new HWPFDocument(poifs);
                // Accessing properties will trigger password check
                doc.getSummaryInformation();
                return false; // Not password protected
            } catch (EncryptedDocumentException e) {
                return true; // Password protected
            }
        } else if (contentType.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) { // Then handle DOCX files
            try (XWPFDocument docx = new XWPFDocument(inputStream)) {
                // Accessing properties will trigger password check
                docx.getProperties().getCoreProperties().getTitle();
                return false; // Not password protected
            } catch (EncryptedDocumentException e) {
                return true; // Password protected
            }
        } else {
            // Handle unsupported file formats
            throw new IllegalArgumentException("Unsupported file format: " + contentType);
        }
    } catch (Exception e) {
        // Handle exceptions
        e.printStackTrace();
        throw new RuntimeException("Error checking password protection", e);
    }
}

尝试过的Apache Tika代码

private boolean isWordPasswordProtected(InputStream inputStream, String contentType) {
    Metadata metadata = new Metadata();
    Parser parser = new AutoDetectParser();

    // To extract metadata only, we use BodyContentHandler with -1 as the maximum characters
    BodyContentHandler handler = new BodyContentHandler(1000);

    ParseContext context = new ParseContext();
    context.set(Parser.class, parser);

    try {
        parser.parse(inputStream, handler, metadata, context);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (SAXException e) {
        throw new RuntimeException(e);
    } catch (TikaException e) {
        throw new RuntimeException(e);
    }

    // Check metadata for encryption-related information
    String encryption = metadata.get("encryption");
    return encryption != null && !encryption.isEmpty();
}

可行实现方案

方案一:Apache POI 正确检测(覆盖所有Office格式)

之前的POI代码问题在于:

  • 对于OOXML格式(.docx/.pptx/.xlsx),直接实例化文档对象不会触发加密检测,需先检查OPCPackage的加密状态
  • 对于旧格式(.doc/.ppt/.xls),可通过检查文件系统条目或尝试打开文档捕获加密异常

以下是统一实现:

import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.xslf.usermodel.XSLFSlideShow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.EncryptedDocumentException;

import java.io.InputStream;
import java.io.IOException;

public class OfficePasswordDetector {

    public boolean isOfficePasswordProtected(InputStream inputStream, String contentType) throws IOException {
        // 处理旧格式(.doc/.ppt/.xls)
        if (contentType.equalsIgnoreCase("application/msword") || 
            contentType.equalsIgnoreCase("application/vnd.ms-powerpoint") || 
            contentType.equalsIgnoreCase("application/vnd.ms-excel")) {
            try (POIFSFileSystem poifs = new POIFSFileSystem(inputStream)) {
                // 旧加密文档会存在"EncryptedPackage"条目
                if (poifs.getRoot().hasEntry("EncryptedPackage")) {
                    return true;
                }
                // 尝试打开文档触发加密检查
                switch (contentType.toLowerCase()) {
                    case "application/msword":
                        new HWPFDocument(poifs);
                        break;
                    case "application/vnd.ms-powerpoint":
                        new HSLFSlideShow(poifs);
                        break;
                    case "application/vnd.ms-excel":
                        new HSSFWorkbook(poifs);
                        break;
                }
                return false;
            } catch (EncryptedDocumentException e) {
                return true;
            } catch (Exception e) {
                // 无法确定时返回false,可根据业务调整
                return false;
            }
        } 
        // 处理OOXML格式(.docx/.pptx/.xlsx)
        else if (contentType.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.wordprocessingml.document") ||
                 contentType.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.presentationml.presentation") ||
                 contentType.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) {
            try {
                OPCPackage pkg = OPCPackage.open(inputStream);
                boolean isEncrypted = pkg.isEncrypted();
                pkg.close();
                return isEncrypted;
            } catch (InvalidFormatException | EncryptedDocumentException e) {
                return true;
            } catch (Exception e) {
                return false;
            }
        } else {
            throw new IllegalArgumentException("Unsupported file format: " + contentType);
        }
    }
}

方案二:优化Apache Tika检测

Tika默认解析可能无法正确提取加密元数据,可通过捕获加密相关异常或检查特定元数据键实现:

import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.BodyContentHandler;
import org.apache.tika.exception.TikaException;
import org.xml.sax.SAXException;
import org.apache.poi.EncryptedDocumentException;

import java.io.InputStream;
import java.io.IOException;

public class TikaPasswordDetector {

    public boolean isOfficePasswordProtected(InputStream inputStream) throws IOException {
        Metadata metadata = new Metadata();
        Parser parser = new AutoDetectParser();
        BodyContentHandler handler = new BodyContentHandler(-1); // 仅提取元数据
        ParseContext context = new ParseContext();
        context.set(Parser.class, parser);

        try {
            parser.parse(inputStream, handler, metadata, context);
            // 检查加密元数据,不同Tika版本键可能有差异
            String encrypted = metadata.get("encrypted");
            return encrypted != null && Boolean.parseBoolean(encrypted);
        } catch (TikaException e) {
            // 捕获POI加密异常的包装类
            if (e.getCause() instanceof EncryptedDocumentException) {
                return true;
            }
            throw new IOException("Failed to parse document", e);
        } catch (SAXException e) {
            throw new IOException("SAX parsing error", e);
        }
    }
}

注意事项

  • 确保引入完整的Apache POI依赖:poi(旧格式)、poi-ooxml(新格式)、poi-scratchpad(旧版Word/PowerPoint)
  • 使用try-with-resources管理InputStream和文档对象,避免资源泄漏
  • 异常处理逻辑可根据业务需求调整,比如无法确定加密状态时返回特定标识

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 11:12:16