Java 中文官方教程 2022 版(四十)(3)https://developer.aliyun.com/article/1488168
使用属性
译文:
docs.oracle.com/javase/tutorial/jaxp/properties/usingProps.html
本节重点介绍了 JAXP 1.5 中引入的新属性。
何时使用属性
只有处理不受信任的 XML 内容的应用程序才需要限制获取外部资源。不处理不受信任内容的内部系统和应用程序不需要关注新的限制或进行任何更改。自 7u40 和 JDK8 默认没有对此类限制的要求,应用程序在升级到 7u40 和 JDK8 时不会出现行为变化。
对于处理不受信任的 XML 输入、Schema 或样式表的应用程序,如果已经存在安全措施,比如启用 Java 安全管理器仅授予受信任的外部连接,或者使用解析器解析实体,则不需要 JAXP 1.5 中添加的新功能。
然而,JAXP 1.5 确实为没有安全管理器运行的系统和应用程序提供了直接的保护。对于这类应用程序,可以考虑使用下面详细描述的新功能来进行限制。
通过 API 设置属性
当改变代码可行时,通过 JAXP 工厂或解析器设置新属性是启用限制的最佳方式。属性可以通过以下接口设置:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setAttribute(name, value); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser parser = spf.newSAXParser(); parser.setProperty(name, value); XMLInputFactory xif = XMLInputFactory.newInstance(); xif.setProperty(name, value); SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage); schemaFactory.setProperty(name, value); TransformerFactory factory = TransformerFactory.newInstance(); factory.setAttribute(name, value);
以下是一个将 DOM 解析器限制为仅本地连接的外部 DTD 的示例:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { dbf.setAttribute({{XMLConstants.ACCESS_EXTERNAL_DTD}}, "file, jar:file"); } catch (IllegalArgumentException e) { //jaxp 1.5 feature not supported }
当代码更改可行,并且对于新开发,建议设置新属性如上所示。通过这种方式设置属性,应用程序可以确保无论部署到较旧还是较新版本的 JDK,或者通过系统属性或jaxp.properties
设置属性,都能保持所需的行为。
使用系统属性
如果改变代码不可行,系统属性可能会有用。
如果希望为整个 JDK/JRE 调用设置限制,可以在命令行上设置系统属性;如果仅需要部分应用程序,可以在该部分之前设置系统属性,然后在之后清除。例如,以下代码展示了如何使用系统属性:
//allow resolution of external schemas System.setProperty("javax.xml.accessExternalSchema", "file, http"); //this setting will affect all processing after it's set some processing here //after it's done, clear the property System.clearProperty("javax.xml.accessExternalSchema");
使用 jaxp.properties
jaxp.properties
是一个普通的配置文件。它位于${java.home}/lib/jaxp.properties
,其中 java.home
是 JRE 安装目录,例如,[安装目录路径]/jdk7/jre
。
可通过将以下行添加到 jaxp.properties 文件来设置外部访问限制:
javax.xml.accessExternalStylesheet=file, http
设置此项后,所有 JDK/JRE 的调用将遵守加载外部样式表的限制。
对于不希望允许 XML 处理器进行任何外部连接的系统,此功能可能很有用,此时,所有三个属性可以设置为,例如,仅文件。
错误处理
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/error.html
由于这些属性是当前版本的新功能,建议应用程序捕获适合接口的异常,例如,在以下示例中捕获 SAXException。在旧版本上,应用程序可能正常工作,例如,示例代码包含以下方法,用于检测是否使用支持新属性的 JDK 版本或 JAXP 实现运行示例:
public boolean isNewPropertySupported() { try { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser parser = spf.newSAXParser(); parser.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "file"); } catch (ParserConfigurationException ex) { fail(ex.getMessage()); } catch (SAXException ex) { String err = ex.getMessage(); if (err.indexOf("Property 'http://javax.xml.XMLConstants/property/accessExternalDTD' is not recognized.") > -1) { //expected, jaxp 1.5 not supported return false; } } return true; }
如果由于新属性设置的限制而拒绝访问外部资源,则将以以下格式抛出异常并带有错误信息:
[type of construct]: Failed to read [type of construct] "[name of the external resource]", because "[type of restriction]" access is not allowed due to restriction set by the [property name] property.
例如,如果由于限制只允许使用 http 协议而拒绝获取外部 DTD,如下所示:parser.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "file");
,并且解析器解析包含对"http://java.sun.com/dtd/properties.dtd"
的外部引用的 XML 文件,则错误消息将如下所示:
External DTD: Failed to read external DTD ''http://java.sun.com/dtd/properties.dtd'', because ''http'' access is not allowed due to restriction set by the accessExternalDTD property.
StAX
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/stax.html
StAX、JSR 173 的规范尚不支持新属性。然而,在 JAXP 的上下文中,StAX 确实包括对这些属性的支持。设置新属性类似于 SAX 或 DOM,但通过 XMLInputFactory,如下所示:
XMLInputFactory xif = XMLInputFactory.newInstance(); xif.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "file");
存在于 StAX、JSR 173 规范中指定的属性和特性将优先于新的 JAXP 属性。例如,当 SupportDTD 属性设置为 false 时,将导致程序在输入文件包含 DTD 之前无法解析时抛出异常。对于使用 SupportDTD 属性禁用 DTD 的应用程序,新属性的添加不会产生影响。
结论
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/conclusion.html
JAXP 1.5 提供了新的属性,让用户可以控制获取外部资源。使用新属性与其他现有属性相同,只是这些属性与相应的系统属性和jaxp.properties
一起提供,以便它们可以用于系统范围的限制或权限。
参考资料
原文:
docs.oracle.com/javase/tutorial/jaxp/properties/references.html
欲了解更多信息,请参阅以下资源:
- JSR 206 JAXP 1.5 maintenance review
- 7u40 Release Notes
- JDK 8 API and Documentation
- JDK 7 API and Documentation
- JEP 185
Lesson: 处理限制
XML 处理有时可能是一个消耗大量内存的操作。应用程序,特别是那些接受来自不受信任来源的 XML、XSD 和 XSL 的应用程序,应该通过使用 JDK 提供的 JAXP 处理限制来防范过度的内存消耗。
开发人员应该评估他们应用程序的需求和运行环境,以确定系统配置的可接受限制,并相应地设置这些限制。与大小相关的限制可用于防止处理畸形的 XML 源时消耗大量内存,而EntityExpansionLimit
将允许应用程序在可接受水平下控制内存消耗。
在本教程中,您将了解这些限制,并学习如何正确使用它们。
处理限制定义
以下列表描述了 JDK 支持的 JAXP XML 处理限制。这些限制可以通过工厂 API、系统属性和jaxp.properties
文件指定。
entityExpansionLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit |
定义 | 限制实体扩展的数量。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 64000 |
系统属性 | jdk.xml.entityExpansionLimit |
自从 | 7u45, 8 |
elementAttributeLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/elementAttributeLimit |
定义 | 限制元素可以拥有的属性数量。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 10000 |
系统属性 | jdk.xml.elementAttributeLimit |
自从 | 7u45, 8 |
maxOccurLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/maxOccurLimit |
定义 | 限制在构建包含值不是"unbounded"的maxOccurs 属性的 W3C XML Schema 的语法时可以创建的内容模型节点的数量。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 5000 |
系统属性 | jdk.xml.maxOccurLimit |
自从 | 7u45, 8 |
totalEntitySizeLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/totalEntitySizeLimit |
定义 | 限制包含通用实体和参数实体的所有实体的总大小。大小是所有实体的聚合计算。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 5x10⁷ |
系统属性 | jdk.xml.totalEntitySizeLimit |
自从 | 7u45, 8 |
maxGeneralEntitySizeLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/maxGeneralEntitySizeLimit |
定义 | 限制任何通用实体的最大大小。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 0 |
系统属性 | jdk.xml.maxGeneralEntitySizeLimit |
自从 | 7u45, 8 |
maxParameterEntitySizeLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/maxParameterEntitySizeLimit |
定义 | 限制任何参数实体的最大大小,包括嵌套多个参数实体的结果。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 1000000 |
系统属性 | jdk.xml.maxParameterEntitySizeLimit |
自 JDK 7u45, 8 起 |
entityReplacementLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/entityReplacementLimit |
定义 | 限制所有实体引用中节点的总数。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 3000000 |
系统属性 | jdk.xml.entityReplacementLimit |
自 JDK 7u111, 8u101 起 |
maxElementDepth
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/maxElementDepth |
定义 | 限制最大元素深度。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 0 |
系统属性 | jdk.xml.maxElementDepth |
自 JDK 7u65, 8u11 起 |
maxXMLNameLimit
属性 | 描述 |
名称 | http://www.oracle.com/xml/jaxp/properties/maxXMLNameLimit |
定义 | 限制 XML 名称的最大大小,包括元素名称、属性名称和命名空间前缀和 URI。 |
值 | 一个正整数。小于或等于 0 的值表示没有限制。如果值不是整数,则会抛出NumericFormatException 异常。 |
默认值 | 1000 |
系统属性 | jdk.xml.maxXMLNameLimit |
自 JDK 7u91, 8u65 起 |
旧版系统属性
这些属性自 JDK 5.0 和 6 起被引入,并继续为向后兼容性而受支持。
系统属性 | 自 JDK 5.0 和 6 起 | 新系统属性 |
entityExpansionLimit | 1.5 | jdk.xml.entityExpansionLimit |
elementAttributeLimit | 1.5 | jdk.xml.elementAttributeLimit |
maxOccurLimit | 1.6 | jdk.xml.maxOccur |
{java.home}/lib/jaxp.properties
可以在jaxp.properties
文件中指定系统属性,以定义 JDK 或 JRE 的所有调用的行为。格式为system-property-name=value
。例如:
jdk.xml.maxGeneralEntitySizeLimit=1024
范围和顺序
javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING
(FSP)功能对包括 DOM、SAX、模式验证、XSLT 和 XPath 在内的 XML 处理器是必需的。当 FSP 设置为true
时,建议的默认限制将被强制执行。将 FSP 设置为false
不会改变这些限制。
当 Java 安全管理器存在时,FSP 被设置为 true 且无法关闭。因此,建议的默认限制将被强制执行。
在jaxp.properties
文件中指定的属性会影响 JDK 和 JRE 的所有调用,并将覆盖它们的默认值,或者可能已被 FSP 设置的值。
系统属性在设置时会影响 JDK 和 JRE 的调用,并覆盖默认设置或者在jaxp.properties
中设置的值,或者可能已被 FSP 设置的值。
通过 JAXP 工厂或SAXParser
指定的 JAXP 属性优先于系统属性,jaxp.properties
文件以及FEATURE_SECURE_PROCESSING
。
使用限制
环境评估
评估包括在系统级别考虑应用程序可用的内存量,是否接受和处理来自不受信任来源的 XML、XSD 或 XSL 源,以及在应用程序级别考虑是否使用某些构造(如 DTD)。
内存设置和限制
XML 处理可能非常消耗内存。允许消耗的内存量取决于特定环境中应用程序的要求。必须防止处理格式不正确的 XML 数据消耗过多内存。
默认限制通常设置为允许大多数应用程序的合法 XML 输入,并允许小型硬件系统(如 PC)的内存使用。建议将限制设置为可能的最小值,以便在消耗大量内存之前捕获任何格式不正确的输入。
这些限制是相关的,但并非完全冗余。您应为所有限制设置适当的值:通常限制应设置为比默认值小得多的值。
例如,可以设置ENTITY_EXPANSION_LIMIT
和GENERAL_ENTITY_SIZE_LIMIT
来防止过多的实体引用。但是当扩展和实体大小的确切组合未知时,TOTAL_ENTITY_SIZE_LIMIT
可以作为整体控制。同样,虽然TOTAL_ENTITY_SIZE_LIMIT
控制替换文本的总大小,但如果文本是一个非常大的 XML 块,ENTITY_REPLACEMENT_LIMIT
会限制文本中可以出现的节点总数,并防止系统过载。
通过使用getEntityCountInfo
属性估计限制
为帮助您分析应设置的限制值,提供了一个名为http://www.oracle.com/xml/jaxp/properties/getEntityCountInfo
的特殊属性。以下代码片段显示了使用该属性的示例:
parser.setProperty("http://www.oracle.com/xml/jaxp/properties/getEntityCountInfo", "yes");
查看示例以获取有关下载示例代码的更多信息。
当程序在 W3C MathML 3.0 中运行时,将打印出以下表格:
属性 | 限制 | 总大小 | 大小 | 实体名称 |
ENTITY_EXPANSION_LIMIT |
64000 | 1417 | 0 | null |
MAX_OCCUR_NODE_LIMIT |
5000 | 0 | 0 | null |
ELEMENT_ATTRIBUTE_LIMIT |
10000 | 0 | 0 | null |
TOTAL_ENTITY_SIZE_LIMIT |
50000000 | 55425 | 0 | null |
GENERAL_ENTITY_SIZE_LIMIT |
0 | 0 | 0 | null |
PARAMETER_ENTITY_SIZE_LIMIT |
1000000 | 0 | 7303 | %MultiScriptExpression |
MAX_ELEMENT_DEPTH_LIMIT |
0 | 2 | 0 | null |
MAX_NAME_LIMIT |
1000 | 13 | 13 | null |
ENTITY_REPLACEMENT_LIMIT |
3000000 | 0 | 0 | null |
在此示例中,实体引用的总数,或实体扩展,为 1417;默认限制为 64000。所有实体的总大小为 55425;默认限制为 50000000。在解析所有引用后,最大的参数实体是 %MultiScriptExpression
,长度为 7303;默认限制为 1000000。
如果这是应用程序预计要处理的最大文件,请建议将限制设置为较小的数字。例如,ENTITY_EXPANSION_LIMIT
设置为 2000,TOTAL_ENTITY_SIZE_LIMIT
设置为 100000,PARAMETER_ENTITY_SIZE_LIMIT
设置为 10000。
设置限制
限制可以像其他 JAXP 属性一样设置。它们可以通过工厂方法或解析器设置:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setAttribute(name, value); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser parser = spf.newSAXParser(); parser.setProperty(name, value); XMLInputFactory xif = XMLInputFactory.newInstance(); xif.setProperty(name, value); SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage); schemaFactory.setProperty(name, value); TransformerFactory factory = TransformerFactory.newInstance(); factory.setAttribute(name, value);
以下示例显示了如何使用 DocumentBuilderFactory
设置限制:
dbf.setAttribute(JDK_ENTITY_EXPANSION_LIMIT, "2000"); dbf.setAttribute(TOTAL_ENTITY_SIZE_LIMIT, "100000"); dbf.setAttribute(PARAMETER_ENTITY_SIZE_LIMIT, "10000"); dbf.setAttribute(JDK_MAX_ELEMENT_DEPTH, "100");
使用系统属性
如果更改代码不可行,系统属性可能很有用。
要为整个 JDK 或 JRE 的调用设置限制,请在命令行上设置系统属性。要仅为应用程序的一部分设置限制,可以在该部分之前设置系统属性,并在之后清除。以下代码显示了如何使用系统属性:
public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit"; //set limits using system property System.setProperty(SP_GENERAL_ENTITY_SIZE_LIMIT, "2000"); //this setting will affect all processing after it's set ... //after it is done, clear the property System.clearProperty(SP_GENERAL_ENTITY_SIZE_LIMIT);
请注意,属性的值应为整数。如果输入的值不包含可解析的整数,将抛出 NumberFormatException
;请参阅方法 parseInt(String)
。
查看 示例 以获取有关下载示例代码的更多信息。
使用 jaxp.properties
文件
jaxp.properties
文件是一个配置文件。通常位于 ${*java.home*}/lib/jaxp.properties
,其中 *java.home*
是 JRE 安装目录,例如,[安装目录路径]/jdk8/jre。
可通过向 jaxp.properties
文件添加以下行来设置限制:
jdk.xml.maxGeneralEntitySizeLimit=2000
请注意,属性名称与系统属性相同,并具有前缀 jdk.xml
。属性的值应为整数。如果输入的值不包含可解析的整数,将抛出 NumberFormatException
;请参阅方法 parseInt(String)
。
当在文件中设置属性时,所有 JDK 和 JRE 的调用都将遵守限制。