¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * Copyright (c) 2018-2028, DreamLu All rights reserved. |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are met: |
| | | * |
| | | * Redistributions of source code must retain the above copyright notice, |
| | | * this list of conditions and the following disclaimer. |
| | | * Redistributions in binary form must reproduce the above copyright |
| | | * notice, this list of conditions and the following disclaimer in the |
| | | * documentation and/or other materials provided with the distribution. |
| | | * Neither the name of the dreamlu.net developer nor the names of its |
| | | * contributors may be used to endorse or promote products derived from |
| | | * this software without specific prior written permission. |
| | | * Author: DreamLu 墿¥æ¢¦ (596392912@qq.com) |
| | | */ |
| | | package org.springblade.core.tool.utils; |
| | | |
| | | import org.springframework.lang.Nullable; |
| | | import org.w3c.dom.Document; |
| | | import org.w3c.dom.Element; |
| | | import org.w3c.dom.Node; |
| | | import org.w3c.dom.NodeList; |
| | | import org.xml.sax.InputSource; |
| | | import org.xml.sax.SAXException; |
| | | |
| | | import javax.xml.namespace.QName; |
| | | import javax.xml.parsers.DocumentBuilder; |
| | | import javax.xml.parsers.DocumentBuilderFactory; |
| | | import javax.xml.parsers.ParserConfigurationException; |
| | | import javax.xml.xpath.XPath; |
| | | import javax.xml.xpath.XPathConstants; |
| | | import javax.xml.xpath.XPathExpressionException; |
| | | import javax.xml.xpath.XPathFactory; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.io.StringReader; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * xpathè§£æxml |
| | | * |
| | | * <pre> |
| | | * ææ¡£å°åï¼ |
| | | * http://www.w3school.com.cn/xpath/index.asp |
| | | * </pre> |
| | | * |
| | | * @author L.cm |
| | | */ |
| | | public class XmlUtil { |
| | | private final XPath path; |
| | | private final Document doc; |
| | | |
| | | private XmlUtil(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException { |
| | | DocumentBuilderFactory dbf = getDocumentBuilderFactory(); |
| | | DocumentBuilder db = dbf.newDocumentBuilder(); |
| | | doc = db.parse(inputSource); |
| | | path = getXPathFactory().newXPath(); |
| | | } |
| | | |
| | | /** |
| | | * å建工å
·ç±» |
| | | * |
| | | * @param inputSource inputSource |
| | | * @return XmlUtil |
| | | */ |
| | | private static XmlUtil create(InputSource inputSource) { |
| | | try { |
| | | return new XmlUtil(inputSource); |
| | | } catch (ParserConfigurationException | SAXException | IOException e) { |
| | | throw Exceptions.unchecked(e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转æ¢å·¥å
·ç±» |
| | | * |
| | | * @param inputStream inputStream |
| | | * @return XmlUtil |
| | | */ |
| | | public static XmlUtil of(InputStream inputStream) { |
| | | InputSource inputSource = new InputSource(inputStream); |
| | | return create(inputSource); |
| | | } |
| | | |
| | | /** |
| | | * 转æ¢å·¥å
·ç±» |
| | | * |
| | | * @param xmlStr xmlStr |
| | | * @return XmlUtil |
| | | */ |
| | | public static XmlUtil of(String xmlStr) { |
| | | StringReader sr = new StringReader(xmlStr.trim()); |
| | | InputSource inputSource = new InputSource(sr); |
| | | XmlUtil xmlUtil = create(inputSource); |
| | | IoUtil.closeQuietly(sr); |
| | | return xmlUtil; |
| | | } |
| | | |
| | | /** |
| | | * 转æ¢è·¯å¾ |
| | | * |
| | | * @param expression è¡¨è¾¾å¼ |
| | | * @param item å®ä½ |
| | | * @param returnType è¿åç±»å |
| | | * @return Object |
| | | */ |
| | | private Object evalXPath(String expression, @Nullable Object item, QName returnType) { |
| | | item = null == item ? doc : item; |
| | | try { |
| | | return path.evaluate(expression, item, returnType); |
| | | } catch (XPathExpressionException e) { |
| | | throw Exceptions.unchecked(e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·åString |
| | | * |
| | | * @param expression è·¯å¾ |
| | | * @return {String} |
| | | */ |
| | | public String getString(String expression) { |
| | | return (String) evalXPath(expression, null, XPathConstants.STRING); |
| | | } |
| | | |
| | | /** |
| | | * è·åBoolean |
| | | * |
| | | * @param expression è·¯å¾ |
| | | * @return {String} |
| | | */ |
| | | public Boolean getBoolean(String expression) { |
| | | return (Boolean) evalXPath(expression, null, XPathConstants.BOOLEAN); |
| | | } |
| | | |
| | | /** |
| | | * è·åNumber |
| | | * |
| | | * @param expression è·¯å¾ |
| | | * @return {Number} |
| | | */ |
| | | public Number getNumber(String expression) { |
| | | return (Number) evalXPath(expression, null, XPathConstants.NUMBER); |
| | | } |
| | | |
| | | /** |
| | | * è·åæä¸ªèç¹ |
| | | * |
| | | * @param expression è·¯å¾ |
| | | * @return {Node} |
| | | */ |
| | | public Node getNode(String expression) { |
| | | return (Node) evalXPath(expression, null, XPathConstants.NODE); |
| | | } |
| | | |
| | | /** |
| | | * è·ååèç¹ |
| | | * |
| | | * @param expression è·¯å¾ |
| | | * @return NodeList |
| | | */ |
| | | public NodeList getNodeList(String expression) { |
| | | return (NodeList) evalXPath(expression, null, XPathConstants.NODESET); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * è·åString |
| | | * |
| | | * @param node èç¹ |
| | | * @param expression ç¸å¯¹äºnodeçè·¯å¾ |
| | | * @return {String} |
| | | */ |
| | | public String getString(Object node, String expression) { |
| | | return (String) evalXPath(expression, node, XPathConstants.STRING); |
| | | } |
| | | |
| | | /** |
| | | * è·å |
| | | * |
| | | * @param node èç¹ |
| | | * @param expression ç¸å¯¹äºnodeçè·¯å¾ |
| | | * @return {String} |
| | | */ |
| | | public Boolean getBoolean(Object node, String expression) { |
| | | return (Boolean) evalXPath(expression, node, XPathConstants.BOOLEAN); |
| | | } |
| | | |
| | | /** |
| | | * è·å |
| | | * |
| | | * @param node èç¹ |
| | | * @param expression ç¸å¯¹äºnodeçè·¯å¾ |
| | | * @return {Number} |
| | | */ |
| | | public Number getNumber(Object node, String expression) { |
| | | return (Number) evalXPath(expression, node, XPathConstants.NUMBER); |
| | | } |
| | | |
| | | /** |
| | | * è·åæä¸ªèç¹ |
| | | * |
| | | * @param node èç¹ |
| | | * @param expression è·¯å¾ |
| | | * @return {Node} |
| | | */ |
| | | public Node getNode(Object node, String expression) { |
| | | return (Node) evalXPath(expression, node, XPathConstants.NODE); |
| | | } |
| | | |
| | | /** |
| | | * è·ååèç¹ |
| | | * |
| | | * @param node èç¹ |
| | | * @param expression ç¸å¯¹äºnodeçè·¯å¾ |
| | | * @return NodeList |
| | | */ |
| | | public NodeList getNodeList(Object node, String expression) { |
| | | return (NodeList) evalXPath(expression, node, XPathConstants.NODESET); |
| | | } |
| | | |
| | | /** |
| | | * é对没æåµå¥èç¹çç®åå¤ç |
| | | * |
| | | * @return mapéå |
| | | */ |
| | | public Map<String, String> toMap() { |
| | | Element root = doc.getDocumentElement(); |
| | | Map<String, String> params = new HashMap<>(16); |
| | | |
| | | // å°èç¹å°è£
æmapå½¢å¼ |
| | | NodeList list = root.getChildNodes(); |
| | | for (int i = 0; i < list.getLength(); i++) { |
| | | Node node = list.item(i); |
| | | if (node instanceof Element) { |
| | | params.put(node.getNodeName(), node.getTextContent()); |
| | | } |
| | | } |
| | | return params; |
| | | } |
| | | |
| | | private static volatile boolean preventedXXE = false; |
| | | |
| | | private static DocumentBuilderFactory getDocumentBuilderFactory() throws ParserConfigurationException { |
| | | DocumentBuilderFactory dbf = XmlUtil.XmlHelperHolder.documentBuilderFactory; |
| | | if (!preventedXXE) { |
| | | preventXXE(dbf); |
| | | } |
| | | return dbf; |
| | | } |
| | | |
| | | /** |
| | | * preventXXE |
| | | * |
| | | * @param dbf |
| | | * @throws ParserConfigurationException |
| | | */ |
| | | private static void preventXXE(DocumentBuilderFactory dbf) throws ParserConfigurationException { |
| | | // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented |
| | | // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl |
| | | dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
| | | |
| | | // If you can't completely disable DTDs, then at least do the following: |
| | | // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities |
| | | // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities |
| | | |
| | | // JDK7+ - http://xml.org/sax/features/external-general-entities |
| | | dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); |
| | | |
| | | // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities |
| | | // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities |
| | | |
| | | // JDK7+ - http://xml.org/sax/features/external-parameter-entities |
| | | dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
| | | |
| | | // Disable external DTDs as well |
| | | dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); |
| | | |
| | | // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks" |
| | | dbf.setXIncludeAware(false); |
| | | dbf.setExpandEntityReferences(false); |
| | | preventedXXE = true; |
| | | } |
| | | |
| | | private static XPathFactory getXPathFactory() { |
| | | return XmlUtil.XmlHelperHolder.xPathFactory; |
| | | } |
| | | |
| | | /** |
| | | * å
é¨ç±»åä¾ |
| | | */ |
| | | private static class XmlHelperHolder { |
| | | private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); |
| | | private static XPathFactory xPathFactory = XPathFactory.newInstance(); |
| | | } |
| | | |
| | | } |