代码之家  ›  专栏  ›  技术社区  ›  Kaito Kid

XmlWriter无法正确生成所需的命名空间

  •  2
  • Kaito Kid  · 技术社区  · 7 年前

    <samlp:AuthnRequest
            xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
            xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
            IssueInstant="2018-07-04T19:19:53.284Z"
            ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
            Version="2.0">
        <samlp:NameIDPolicy
                AllowCreate="true"
                Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"/>
    </samlp:AuthnRequest>
    

    很明显它因为匿名原因丢失了很多标签。

    我在用 XmlWriter .

    System.Xml.XmlException: 'The prefix '' cannot be redefined from 'samlp' to
    'urn:oasis:names:tc:SAML:2.0:assertion' within the same start element tag.'
    

    以下是我的一些尝试和结果。

    尝试1

    using (XmlWriter xw = XmlWriter.Create(sw, xws))
    {
        xw.WriteStartElement("AuthnRequest", "samlp");
        xw.WriteAttributeString("xmlns", "samlp", null, "urn:oasis:names:tc:SAML:2.0:protocol");
        xw.WriteAttributeString("xmlns", "urn:oasis:names:tc:SAML:2.0:assertion");
        // ...
    }
    

    结果#1

    Crashed with the above message.
    

    尝试2

    using (XmlWriter xw = XmlWriter.Create(sw, xws))
    {
        xw.WriteStartElement("AuthnRequest", "samlp");
        xw.WriteAttributeString("xmlns", "samlp", null, "urn:oasis:names:tc:SAML:2.0:protocol");
        // ...
    }
    

    结果#2

    <AuthnRequest
        xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
        ForceAuthn="false" ID="ID_4f85b6d1-a839-4899-972c-12275bf8711c"
        IssueInstant="2018-08-15T18:23:49Z"
        ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
        Version="2.0"
        xmlns="samlp">
        ...
    

    具体来说,我在生成此属性时遇到了很多问题:

    xmlns="urn:oasis:names:tc:SAML:2.0:assertion"
    

    不是没出现就是错了。

    我无法找出我遗漏了什么,以便按我需要的方式生成它。

    2 回复  |  直到 7 年前
        1
  •  2
  •   dbc    7 年前

    以下代码可以正常工作并成功写入所需的XML:

    var issueInstant = DateTime.Parse("2018-07-04T19:19:53.284Z", CultureInfo.InvariantCulture);
    
    using (var xw = XmlWriter.Create(sw, xws))
    {
        var samplNs = "urn:oasis:names:tc:SAML:2.0:protocol";
        var defaultNs = "urn:oasis:names:tc:SAML:2.0:assertion";
    
        // XmlWriter.WriteStartElement(String prefix, String localName, String ns)
        xw.WriteStartElement("samlp", "AuthnRequest", samplNs);
    
        // Write the xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" namespace.
        // Actually this is redundant as the attribute will be automatically be written at the end of the
        // attribute list do to the previous call to xw.WriteStartElement().  Call it here only if, for some
        // reason, you need to control the attribute order.
        xw.WriteAttributeString("xmlns", "samlp", null, samplNs);               
    
        // Write the default namespace
        xw.WriteAttributeString("xmlns", defaultNs);
    
        // Write attribute values.              
        xw.WriteAttributeString("IssueInstant", XmlConvert.ToString(issueInstant, XmlDateTimeSerializationMode.Utc));
        xw.WriteAttributeString("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
        xw.WriteAttributeString("Version", "2.0");
    
        // Write samlp:NameIDPolicy
        // No need to specify prefix since it is specified in the document root.
        xw.WriteStartElement("NameIDPolicy", samplNs);
        xw.WriteAttributeString("AllowCreate", XmlConvert.ToString(true));
        xw.WriteAttributeString("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
    
        // Write the end of NameIDPolicy
        xw.WriteEndElement();
    
        // Write the end of AuthnRequest
        xw.WriteEndElement();
    }       
    

    笔记:

    • 方法 XmlWriter.WriteStartElement(String localName, String ns) 接受 命名空间 作为第二个参数,但是您传递的是命名空间 "samlp" . 所以那没用。

    • 但是打电话

      xw.WriteStartElement("AuthnRequest", "urn:oasis:names:tc:SAML:2.0:protocol"); 
      

      也会失败,因为这样做会建立 "urn:oasis:names:tc:SAML:2.0:protocol" 作为 违约 sampl XmlWriter.WriteStartElement(String prefix, String localName, String ns) 必须使用。

    • 已经明确地写了 AuthnRequest XmlWriter 将在属性列表的末尾自动为您执行此操作。如果要控制名称空间属性在属性列表中的位置,只需手动编写该名称空间属性。然而,根据 XML Standard :

      请注意,开始标记或空元素标记中属性规范的顺序并不重要。

      所以,你可以跳过这个。

    • 方法来自 XmlConvert 类可用于正确地将非字符串原语从XML转换为XML。

    样品小提琴: https://dotnetfiddle.net/99hu1I .

        2
  •  1
  •   jdweng    7 年前

    我发现当有复杂的名称空间时,解析一个字符串要容易得多。使用xml linq:

                string xml =
                    "<samlp:AuthnRequest" +
                        " xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"" +
                        " xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\"" +
                        " IssueInstant=\"2018-07-04T19:19:53.284Z\"" +
                        " ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"" +
                        " Version=\"2.0\">" +
                        "<samlp:NameIDPolicy" +
                           " AllowCreate=\"true\"" +
                           " Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\"/>" +
                    "</samlp:AuthnRequest>";
    
                XDocument doc = XDocument.Parse(xml);