PDF嵌入JavaScript验证失败求助:依赖与检测代码指导
问题:iText添加PDF JavaScript后无法检测到
我使用iText库给PDF文件添加JavaScript,但自己编写的检测代码无法识别已添加的JS。以下是相关代码和需求:
添加JavaScript的代码
@PostMapping("/addJSInPdf") public ResponseEntity<String> addJSInPdf(@RequestParam("file") MultipartFile file) { try { ByteArrayInputStream inputStream = new ByteArrayInputStream(file.getBytes()); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PdfReader reader = new PdfReader(inputStream); Document document = new Document(reader.getPageSize(1)); PdfWriter writer = PdfWriter.getInstance(document, outputStream); document.open(); PdfContentByte contentByte = writer.getDirectContent(); PdfImportedPage importedPage; int totalPages = reader.getNumberOfPages(); for (int i = 1; i <= totalPages; i++) { importedPage = writer.getImportedPage(reader, i); contentByte.addTemplate(importedPage, 0, 0); } writer.addJavaScript("app.alert('This is an alert box in the PDF!');"); document.close(); String base64EncodedPdf = Base64.getEncoder().encodeToString(outputStream.toByteArray()); return ResponseEntity.ok("{\"pdf\":\"" + base64EncodedPdf + "\"}"); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body("{\"error\":\"Internal Server Error\"}"); } }
原检测JavaScript的代码
public boolean validatePDF(byte[] fileContent) { try { PdfReader reader = new PdfReader(new ByteArrayInputStream(fileContent)); if (containsJavaScript(reader)) { return true; } return false; } catch (IOException e) { e.printStackTrace(); return false; } } private boolean containsJavaScript(PdfReader reader) { try { PdfDictionary catalog = reader.getCatalog(); // 1. Check OpenAction for JavaScript PdfDictionary openAction = catalog.getAsDict(PdfName.OPENACTION); if (openAction != null) { PdfObject jsObject = openAction.get(PdfName.JS); if (jsObject instanceof PdfString) { System.out.println("JavaScript found in OpenAction: " + ((PdfString) jsObject).toUnicodeString()); return true; } } // 2. Check additional actions (AA) for JavaScript PdfDictionary aa = catalog.getAsDict(PdfName.AA); if (aa != null) { for (PdfName actionType : aa.getKeys()) { PdfDictionary action = aa.getAsDict(actionType); PdfObject jsObject = action.get(PdfName.JS); if (jsObject instanceof PdfString) { System.out.println( "JavaScript found in Additional Actions: " + ((PdfString) jsObject).toUnicodeString()); return true; } } } // 3. Traverse through annotations and actions in all pages for (int i = 1; i <= reader.getNumberOfPages(); i++) { PdfDictionary page = reader.getPageN(i); PdfArray annots = page.getAsArray(PdfName.ANNOTS); if (annots != null) { for (PdfObject annotObj : annots) { PdfDictionary annot = (PdfDictionary) PdfReader.getPdfObject(annotObj); PdfObject jsObject = annot.get(PdfName.JS); if (jsObject instanceof PdfString) { System.out.println( "JavaScript found in Annotation: " + ((PdfString) jsObject).toUnicodeString()); return true; } } } } // 4. Check the Names dictionary for JavaScript PdfDictionary names = catalog.getAsDict(PdfName.NAMES); if (names != null) { PdfDictionary embeddedJS = names.getAsDict(PdfName.JAVASCRIPT); if (embeddedJS != null) { PdfArray jsArray = embeddedJS.getAsArray(PdfName.NAMES); if (jsArray != null) { for (int i = 1; i < jsArray.size(); i += 2) { PdfObject jsObject = jsArray.getDirectObject(i); if (jsObject instanceof PdfString) { System.out.println( "JavaScript found in Names: " + ((PdfString) jsObject).toUnicodeString()); return true; } } } } } return false; } catch (Exception e) { e.printStackTrace(); return false; } }
需求
- 提供验证PDF中JavaScript所需的正确依赖;
- 给出能正确检测PDF中嵌入JavaScript的示例代码。
解决方案
1. 正确的依赖
你的代码基于iText 5(开源版本),对应Maven依赖如下:
<dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.3</version> </dependency>
Gradle依赖:
implementation 'com.itextpdf:itextpdf:5.5.13.3'
2. 修正后的JavaScript检测代码
原检测代码失败的核心原因是:writer.addJavaScript()添加的JS被iText存储为PdfStream(而非PdfString),原代码仅检查PdfString导致遗漏。以下是覆盖所有常见JS场景的检测代码:
public boolean validatePDF(byte[] fileContent) { try { PdfReader reader = new PdfReader(new ByteArrayInputStream(fileContent)); return containsJavaScript(reader); } catch (IOException e) { e.printStackTrace(); return false; } } private boolean containsJavaScript(PdfReader reader) { try { PdfDictionary catalog = reader.getCatalog(); // 1. 检测文档级JavaScript(writer.addJavaScript()添加的内容在此) PdfDictionary names = catalog.getAsDict(PdfName.NAMES); if (names != null) { PdfDictionary jsNamesDict = names.getAsDict(PdfName.JAVASCRIPT); if (jsNamesDict != null) { PdfArray jsArray = jsNamesDict.getAsArray(PdfName.NAMES); if (jsArray != null) { for (int i = 1; i < jsArray.size(); i += 2) { PdfObject jsObj = jsArray.getDirectObject(i); String jsContent = extractJsContent(jsObj); if (jsContent != null && !jsContent.trim().isEmpty()) { System.out.println("找到文档级JavaScript: " + jsContent); return true; } } } } } // 2. 检测OpenAction中的JavaScript PdfObject openActionObj = catalog.get(PdfName.OPENACTION); if (openActionObj != null) { if (openActionObj instanceof PdfDictionary) { PdfDictionary action = (PdfDictionary) openActionObj; if (isJavaScriptAction(action)) { String jsContent = extractJsContent(action.get(PdfName.JS)); if (jsContent != null) { System.out.println("找到OpenAction中的JavaScript: " + jsContent); return true; } } } else if (openActionObj instanceof PdfArray) { PdfArray actionArray = (PdfArray) openActionObj; for (PdfObject obj : actionArray) { PdfDictionary action = (PdfDictionary) PdfReader.getPdfObject(obj); if (action != null && isJavaScriptAction(action)) { String jsContent = extractJsContent(action.get(PdfName.JS)); if (jsContent != null) { System.out.println("找到OpenAction序列中的JavaScript: " + jsContent); return true; } } } } } // 3. 检测页面注释中的JavaScript for (int pageNum = 1; pageNum <= reader.getNumberOfPages(); pageNum++) { PdfDictionary page = reader.getPageN(pageNum); PdfArray annots = page.getAsArray(PdfName.ANNOTS); if (annots != null) { for (PdfObject annotObj : annots) { PdfDictionary annot = (PdfDictionary) PdfReader.getPdfObject(annotObj); if (annot == null) continue; // 检测注释的触发动作 PdfDictionary action = annot.getAsDict(PdfName.A); if (action != null && isJavaScriptAction(action)) { String jsContent = extractJsContent(action.get(PdfName.JS)); if (jsContent != null) { System.out.println("找到注释动作中的JavaScript: " + jsContent); return true; } } // 检测注释的额外动作(AA) PdfDictionary annotAA = annot.getAsDict(PdfName.AA); if (annotAA != null) { for (PdfName key : annotAA.getKeys()) { PdfDictionary aaAction = annotAA.getAsDict(key); if (aaAction != null && isJavaScriptAction(aaAction)) { String jsContent = extractJsContent(aaAction.get(PdfName.JS)); if (jsContent != null) { System.out.println("找到注释额外动作中的JavaScript: " + jsContent); return true; } } } } } } } // 4. 检测文档额外动作(AA)中的JavaScript PdfDictionary docAA = catalog.getAsDict(PdfName.AA); if (docAA != null) { for (PdfName key : docAA.getKeys()) { PdfDictionary action = docAA.getAsDict(key); if (action != null && isJavaScriptAction(action)) { String jsContent = extractJsContent(action.get(PdfName.JS)); if (jsContent != null) { System.out.println("找到文档额外动作中的JavaScript: " + jsContent); return true; } } } } return false; } catch (Exception e) { e.printStackTrace(); return false; } } // 判断是否为JavaScript动作 private boolean isJavaScriptAction(PdfDictionary action) { return action != null && PdfName.JAVASCRIPT.equals(action.getAsName(PdfName.S)); } // 从PdfObject提取JS内容,支持PdfString和PdfStream private String extractJsContent(PdfObject obj) throws IOException { if (obj == null) return null; if (obj instanceof PdfString) { return ((PdfString) obj).toUnicodeString(); } else if (obj instanceof PdfStream) { return new String(PdfReader.getStreamBytes((PdfStream) obj), StandardCharsets.UTF_8); } return null; }
关键改进点
- 新增
extractJsContent方法,同时支持提取PdfString和PdfStream类型的JS内容; - 完善了OpenAction的检测,支持单个动作和动作序列两种情况;
- 覆盖了文档级JS、OpenAction、注释动作、文档/注释额外动作等所有常见JS嵌入场景;
- 新增
isJavaScriptAction方法统一判断动作类型,代码更简洁。
内容的提问来源于stack exchange,提问作者Ravi Dobariya
相关产品推荐
相关产品推荐

