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
相关产品推荐
相关产品推荐

