如何将xsd/xsi命名空间声明从子元素移至saml:Assertion元素?
问题
我开发了一个Java + Spring Boot应用,使用jaxws-maven-plugin基于.wsdl和.xsd文件生成源码。发送SOAP请求后,返回的响应与客户端提供的预期示例存在差异。
预期响应
<soapenv:Envelope> <soapenv:Body> <wst13:SecResponse> <wst13:SecToken> <saml:Assertion ID="A-123" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <saml:AttributeStatement> <saml:Attribute Name="ClientID"> <saml:AttributeValue xsi:type="xsd:string">hello</saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement> </saml:Assertion> </wst13:RequestedSecurityToken> </wst13:RequestSecurityTokenResponse> </soapenv:Body> </soapenv:Envelope>
实际响应
<soapenv:Envelope> <soapenv:Body> <wst13:SecResponse> <wst13:SecToken> <saml:Assertion ID="A-123" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"> <saml:AttributeStatement> <saml:Attribute Name="ClientID"> <saml:AttributeValue xsi:type="xsd:string" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">hello</saml:AttributeValue> </saml:Attribute> </saml:AttributeStatement> </saml:Assertion> </wst13:RequestedSecurityToken> </wst13:RequestSecurityTokenResponse> </soapenv:Body> </soapenv:Envelope>
核心差异
预期中xsd和xsi命名空间定义在saml:Assertion元素上,但实际生成的响应里这两个命名空间被放在了saml:AttributeValue元素上。我需要将这两个命名空间声明从saml:AttributeValue移至saml:Assertion元素。
已尝试的方案
- 自定义继承
NamespacePrefixMapper的命名空间映射器,只能把xmlns:xsi移到根元素,不符合需求; - 尝试使用Marshaller监听器,但没找到正确的实现方式。
解决方案
方法1:修改生成的Assertion类(临时验证用)
找到jaxws-maven-plugin生成的Assertion类,在类上添加@XmlNs注解指定xsd和xsi的命名空间:
@XmlType(name = "Assertion", namespace = "urn:oasis:names:tc:SAML:2.0:assertion") @XmlRootElement(namespace = "urn:oasis:names:tc:SAML:2.0:assertion") @XmlNs(prefix = "xsd", namespaceURI = "http://www.w3.org/2001/XMLSchema") @XmlNs(prefix = "xsi", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance") public class Assertion { // 原有代码逻辑 }
⚠️注意:重新生成代码时这个修改会被覆盖,仅适合临时验证效果。
方法2:自定义Marshaller监听器强制添加命名空间
创建一个监听器,在Assertion对象序列化前,手动为对应的XML元素添加命名空间声明:
public class AssertionNamespaceListener extends Marshaller.Listener { @Override public void beforeMarshal(Object source) { if (source instanceof Assertion) { try { // 获取JAXB上下文并定位Assertion元素 JAXBContext context = JAXBContext.newInstance(Assertion.class); JAXBIntrospector introspector = context.createJAXBIntrospector(); QName elementName = introspector.getElementName(source); // 如果Marshaller输出到DOM节点,直接操作节点添加属性 if (source instanceof Node) { Node assertionNode = (Node) source; if (assertionNode instanceof Element) { Element assertionElement = (Element) assertionNode; assertionElement.setAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); assertionElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); } } } catch (JAXBException e) { e.printStackTrace(); } } } }
然后在配置JAX-WS代理时注册这个监听器:
@Bean public JaxWsPortProxyFactoryBean servicePort() { JaxWsPortProxyFactoryBean factory = new JaxWsPortProxyFactoryBean(); // 配置WSDL地址、服务接口等参数... // 获取并配置Marshaller Marshaller marshaller = JAXBContext.newInstance(Assertion.class).createMarshaller(); marshaller.setListener(new AssertionNamespaceListener()); factory.setMarshaller(marshaller); return factory; }
方法3:修改XSD文件(推荐持久化方案)
如果有权限修改对应的SAML XSD文件,在Assertion元素的定义中显式绑定xsd和xsi命名空间:
<xs:element name="Assertion" type="saml:AssertionType"> <xs:annotation> <xs:appinfo> <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" version="2.1"> <jaxb:namespacePrefixes> <jaxb:namespacePrefix prefix="xsd" uri="http://www.w3.org/2001/XMLSchema"/> <jaxb:namespacePrefix prefix="xsi" uri="http://www.w3.org/2001/XMLSchema-instance"/> </jaxb:namespacePrefixes> </jaxb:bindings> </xs:appinfo> </xs:annotation> </xs:element>
这样重新生成Java类时,Assertion类会自动包含这些命名空间配置,Marshaller序列化时会将命名空间声明放在saml:Assertion元素上。
内容的提问来源于stack exchange,提问作者gregof
相关产品推荐
相关产品推荐

