禁用dtd是解决“doctype is disallowed”错误的根本方法:调用setfeature("http://apache.org/xml/features/disallow-doctype-decl", true)强制拒绝doctype声明,并关闭验证和外部dtd加载,防止xxe攻击。

Java SAX解析时遇到“DOCTYPE is disallowed”错误
这是禁用DTD验证最常触发的报错,本质是XML解析器默认开启外部实体和DOCTYPE校验,而你的XML里有/code>声明(哪怕只是<code>这种占位写法)。SAXParserFactory默认不信任任何外部声明,直接抛<code>SAXParseException。
解决方式不是删DTD——很多第三方XML根本没法改源文件。得在解析器创建阶段就切断校验链:
- 调用
setFeature("http://apache.org/xml/features/disallow-doctype-decl", true):强制拒绝DOCTYPE声明(推荐,最彻底) - 同时设
setFeature("http://xml.org/sax/features/validation", false)和setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false):补漏,防止某些JDK版本绕过第一个开关 - 别碰
setValidating(true)——它会自动打开所有校验,和目标完全相反
DocumentBuilder也卡在DTD加载上
DOM解析走的是DocumentBuilderFactory,行为类似但开关名不同。常见现象是解析卡住几秒甚至超时,其实是解析器在尝试HTTP拉取SYSTEM里的dtd地址(比如http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd)。
关键动作是关闭外部DTD加载,并把错误处理器设为空实现:
立即学习“Java免费学习笔记(深入)”;
- 调用
setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) - 调用
setFeature("http://xml.org/sax/features/validation", false) - 给
DocumentBuilder设置空ErrorHandler:builder.setErrorHandler(new DefaultHandler()),否则即使不加载,警告也会打到控制台 - 避免使用
builder.setEntityResolver(...)自定义解析器——除非你真要拦截并重写,否则多此一举还可能引入死循环
为什么不用JAXB或Jackson XML?
它们底层仍依赖SAX或DOM,禁用逻辑一模一样。比如JAXBContext创建的Unmarshaller,必须先拿到底层SAXParser或配置DocumentBuilderFactory。Jackson XML的XmlMapper则需通过XmlFactory设置:
XmlFactory factory = new XmlFactory();-
factory.configure(XXEFeature.DISALLOW_DOCTYPE_DECL, true);(Jackson 2.12+) - 旧版Jackson得用
factory.setProperty("http://apache.org/xml/features/disallow-doctype-decl", true) - 别以为换库就自动安全——所有Java XML库共享同一套Xerces底层,默认都开DTD
禁用后会影响XPath或XSLT吗?
不影响。XPath查询、XSLT转换操作的是已加载进内存的DOM树或SAX事件流,和原始XML是否含DOCTYPE无关。但要注意:如果XML里有定义,禁用DTD后这些实体将无法解析,变成未定义符号或直接报错。这时候不能简单禁用,得用<code>EntityResolver手动映射本地dtd文件。
真正要警惕的是生产环境XML来源不可控——比如用户上传、第三方API返回。此时禁用DTD不仅是提速,更是防XXE攻击的硬性要求。一个没处理的SYSTEM声明,可能让服务器去读/etc/passwd。










