创建 QName 异常时,Opensaml unmarshallMessage 给出的 opensaml 本地部分不能为“null”

Posted

技术标签:

【中文标题】创建 QName 异常时,Opensaml unmarshallMessage 给出的 opensaml 本地部分不能为“null”【英文标题】:Opensaml unmarshallMessage giving opensaml local part cannot be "null" when creating a QName Exception 【发布时间】:2021-07-30 17:49:54 【问题描述】:

我正在使用 Spring SAML,来自 IDP 响应我收到 issuer 作为属性 ID,所以我想在 spring saml 接收后更改响应,所以我已覆盖方法 unmarshall,它将解析消息并为此更改 xml 元素。这样做时,如果我在解析解组后直接使用 Element,它正在工作,但如果我进行 dom 操作,则会给出以下异常。我不明白为什么它在第一个代码中抛出异常,但在下面附加的第二个代码中却没有。如何在不再次转换为字符串并获取 dom 对象的情况下使其工作。

Exception in thread "main" java.lang.IllegalArgumentException: local part cannot be "null" when creating a QName
    at java.xml/javax.xml.namespace.QName.<init>(QName.java:185)
    at java.xml/javax.xml.namespace.QName.<init>(QName.java:129)
    at org.opensaml.xml.util.XMLHelper.constructQName(XMLHelper.java:433)
    at org.opensaml.xml.util.XMLHelper.getNodeQName(XMLHelper.java:171)
    at org.opensaml.xml.io.UnmarshallerFactory.getUnmarshaller(UnmarshallerFactory.java:81)
    at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshallChildElement(AbstractXMLObjectUnmarshaller.java:334)
    at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshall(AbstractXMLObjectUnmarshaller.java:127)
    at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshallChildElement(AbstractXMLObjectUnmarshaller.java:355)
    at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshall(AbstractXMLObjectUnmarshaller.java:127)
    at org.opensaml.ws.message.decoder.BaseMessageDecoder.unmarshallMessage(unmarshallMessage.java:208)

    对于下面的代码,如上所述,它会抛出异常。

    protected XMLObject unmarshallMessage(InputStream messageStream) throws MessageDecodingException 
         log.debug("Parsing message stream into DOM document");
    
         try 
             Document messageDoc = parserPool.parse(messageStream);
             Element messageElem = messageDoc.getDocumentElement();
             Element elementsByTagName = (Element) messageElem.getElementsByTagName("saml2:Assertion").item(0);
             Element issuer = messageDoc.createElement("saml2:Issuer");
             issuer.setTextContent("emIDAM");
             issuer.setAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
             log.debug("Unmarshalling message DOM");
             Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(messageElem);
             if (unmarshaller == null) 
                 log.debug("Unable to unmarshall message, no unmarshaller registered for message element "
                         + XMLHelper.getNodeQName(messageElem));
                 throw new MessageDecodingException(
                         "Unable to unmarshall message, no unmarshaller registered for message element "
                                 + XMLHelper.getNodeQName(messageElem));
             
             XMLObject message = unmarshaller.unmarshall(messageElem);
    
             return message;
          catch (XMLParserException e) 
             log.error("Encountered error parsing message into its DOM representation", e);
             throw new MessageDecodingException("Encountered error parsing message into its DOM representation", e);
          catch (UnmarshallingException e) 
             log.error("Encountered error unmarshalling message from its DOM representation", e);
             throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
         
         catch (Exception e) 
             log.warn("Encountered error unmarshalling message from its DOM representation --->", e);
             throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
         
     
    

    但对于下面的代码,它工作正常,因为在这里我再次转换为字符串并创建输入流,然后获取传递给解组的 dom。

    protected XMLObject unmarshallMessage(InputStream messageStream) throws MessageDecodingException 
         log.debug("Parsing message stream into DOM document");
    
         try 
             Document messageDoc = parserPool.parse(messageStream);
             Element messageElem = messageDoc.getDocumentElement();
             Element elementsByTagName = (Element) messageElem.getElementsByTagName("saml2:Assertion").item(0);
             Element issuer = messageDoc.createElement("saml2:Issuer");
             issuer.setTextContent("emIDAM");
             issuer.setAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
    
             //converting again to string and getting dom
             String xml = XMLHelper.nodeToString(messageElem);
             messageStream = new ByteArrayInputStream(xml.getBytes());
             messageDoc = parserPool.parse(messageStream);
             messageElem = messageDoc.getDocumentElement();
    
    
             log.debug("Unmarshalling message DOM");
             Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(messageElem);
             if (unmarshaller == null) 
                 log.debug("Unable to unmarshall message, no unmarshaller registered for message element "
                         + XMLHelper.getNodeQName(messageElem));
                 throw new MessageDecodingException(
                         "Unable to unmarshall message, no unmarshaller registered for message element "
                                 + XMLHelper.getNodeQName(messageElem));
             
             XMLObject message = unmarshaller.unmarshall(messageElem);
    
             return message;
          catch (XMLParserException e) 
             log.error("Encountered error parsing message into its DOM representation", e);
             throw new MessageDecodingException("Encountered error parsing message into its DOM representation", e);
          catch (UnmarshallingException e) 
             log.error("Encountered error unmarshalling message from its DOM representation", e);
             throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
         
         catch (Exception e) 
             log.warn("Encountered error unmarshalling message from its DOM representation --->", e);
             throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
         
     
    

【问题讨论】:

【参考方案1】:

您的问题可能与您的解析器配置有关,可能未配置为支持命名空间。

请在初始化 parserPool 时使用类似于以下代码的代码:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder parserPool = documentBuilderFactory.newDocumentBuilder();

我的猜测是第二个测试成功运行,因为在后台 XMLHelper nodeToString 正在使用 writeNode,而 writeNode 又使用 LSSerializer 来执行实际的 XML 序列化。由于解析器被声明为非命名空间感知,因此生成的 XML 输出和对应的解析 Document 将不包含命名空间信息,或者至少,它包含的信息以这样一种方式,即随后由 Unmarshaller 执行的解析过程认为合适。

此外,请注意,问题可能与您创建新 Element 的方式有关,因为您也没有提供命名空间信息:当您使用 createElement 时,生成的 @987654333 @ 将不包含localNameprefixnamespaceURI,都设置为null。请尝试以下代码:

Document messageDoc = parserPool.parse(messageStream);
Element messageElem = messageDoc.getDocumentElement();
Element assertionElem = (Element) messageElem.getElementsByTagName("saml2:Assertion").item(0);
Element issuerElement = messageDoc.createElementNS("urn:oasis:names:tc:SAML:2.0:assertion", "saml2:Issuer");
Attr formatAttr = messageDoc.createAttributeNS("urn:oasis:names:tc:SAML:2.0:assertion", "saml2:Format");
formatAttr.setNodeValue("urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
issuerElement.setAttributeNode(formatAttr);
Text text = messageDoc.createTextNode("emIDAM");
issuerElement.appendChild(text);
assertionElem.appendChild(issuerElement);

【讨论】:

以上是关于创建 QName 异常时,Opensaml unmarshallMessage 给出的 opensaml 本地部分不能为“null”的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中从给定的 XML 创建 OpenSAML 断言

OpenSAML / Spring 安全设置,因此在 Tomcat 上重新部署有效

Worklight javax/xml/名称空间/QName

org.opensaml.common.SAMLRuntimeException:无法获取 SP 签名密钥

php UNM Google图书搜索

使用 OpenSAML 在 Java 中使用 SAML 2.0 解密加密断言