首先是Saml工具类
SamlUtils
package xuzs.common; import java.io.IOException; import java.io.InputStream; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.opensaml.DefaultBootstrap; import org.opensaml.common.impl.SecureRandomIdentifierGenerator; import org.opensaml.xml.Configuration; import org.opensaml.xml.XMLObject; import org.opensaml.xml.XMLObjectBuilder; import org.opensaml.xml.io.Marshaller; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.io.UnmarshallingException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class SamlUtils { public static final String ACT_REQ = "samlp:ActivateRequest"; public static final String ACT_RESP = "samlp:ActivateResponse"; public static final String LGOUT_REQ = "samlp:LogoutRequest"; public static final String LGOUT_RESP = "samlp:LogoutResponse"; private static DocumentBuilder builder; private static SecureRandomIdentifierGenerator generator; private String issuerURL; public String getIssuerURL() { return issuerURL; } public void setIssuerURL(String issuerURL) { this.issuerURL = issuerURL; } static { try { // 此处初始化所有builder等操作 DefaultBootstrap.bootstrap(); generator = new SecureRandomIdentifierGenerator(); DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); factory.setNamespaceAware(true); builder = factory.newDocumentBuilder(); } catch (Exception ex) { ex.printStackTrace(); } } public SamlUtils() { this(null); } public SamlUtils(String issuerURL) { this.issuerURL = issuerURL; } /** * 产生ID * @return 唯一ID */ public static String generateId(){ return generator.generateIdentifier().substring(1); } /** * 构造对象 * @param cls * @param qname * @return */ @SuppressWarnings("unchecked") public static <T> T create(Class<T> cls, QName qname) { return (T) ((XMLObjectBuilder<?>) Configuration.getBuilderFactory() .getBuilder(qname)).buildObject(qname); } /** */ public static Document asDOMDocument(XMLObject object){ Document document = builder.newDocument(); Marshaller out = Configuration.getMarshallerFactory().getMarshaller( object); try { out.marshall(object, document); } catch (MarshallingException e) { e.printStackTrace(); } return document; } public static XMLObject fromElement(Element element){ XMLObject xmlObject = null; try { xmlObject = Configuration.getUnmarshallerFactory().getUnmarshaller(element) .unmarshall(element); } catch (UnmarshallingException e) { e.printStackTrace(); } return xmlObject; } public static Document parse(InputStream is){ Document doc = null; try { doc = builder.parse(is); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return doc; } /** * 替换Document中指定的Node的名称 * @param doc 要替换的Document * @param oNodeName 被替换的节点名称 * @param tNodeName 要替换为的节点名称 * @return */ public static boolean renameNode(Document doc, String oNodeName, String tNodeName) { NodeList nodes = doc.getElementsByTagName(oNodeName); if (nodes.getLength() > 0) { Node node = nodes.item(0); doc.renameNode(node, node.getNamespaceURI(), tNodeName); return true; } return false; } }
然后是发送的客户端类
SamlMsgClient
package xuzs.common; import org.opensaml.ws.soap.client.BasicSOAPMessageContext; import org.opensaml.ws.soap.client.http.HttpClientBuilder; import org.opensaml.ws.soap.client.http.HttpSOAPClient; import org.opensaml.ws.soap.common.SOAPException; import org.opensaml.ws.soap.soap11.Body; import org.opensaml.ws.soap.soap11.Envelope; import org.opensaml.xml.XMLObject; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.security.SecurityException; public class SamlMsgClient { /** * 对端地址 */ private String serverUrl; protected String getServerUrl() { return serverUrl; } protected void setServerUrl(String serverUrl) { this.serverUrl = serverUrl; } public SamlMsgClient(String serverUrl) { super(); this.serverUrl = serverUrl; } /** * 直接发送samlXml对象,并返回samlXml对象 * * @param reqXmlObj 需要发送的消息体 * @return 结果对象 */ public XMLObject send(XMLObject reqXmlObj) { // 声明返回对象 XMLObject respXmlObj = null; // 构造消息信封 Envelope envelope = SamlUtils.create(Envelope.class, Envelope.DEFAULT_ELEMENT_NAME); Body body = SamlUtils.create(Body.class, Body.DEFAULT_ELEMENT_NAME); // 将请求对象放入信封体 body.getUnknownXMLObjects().add(reqXmlObj); envelope.setBody(body); // 构造SOAP消息上下文,并将信封放入其中 BasicSOAPMessageContext soapContext = new BasicSOAPMessageContext(); soapContext.setOutboundMessage(envelope); // 构造消息发送客户端 HttpClientBuilder clientBuilder = new HttpClientBuilder(); HttpSOAPClient soapClient = new HttpSOAPClient( clientBuilder.buildClient(), new BasicParserPool()); try { // 发送消息体,并返回结果对象 soapClient.send(serverUrl, soapContext); Envelope respEnvelope = (Envelope) soapContext.getInboundMessage(); if (respEnvelope.getBody().getUnknownXMLObjects().size() > 0) { respXmlObj = respEnvelope.getBody().getUnknownXMLObjects().get(0); } } catch (SOAPException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } return respXmlObj; } /** * 发送samlXml对象,但在发送和接收时做消息名称的修改 * * @param reqXmlObj 发送消息体,此处可为LogoutRequest * @param oReqName 请求消息中被替换的节点名称 * @param tReqName 请求消息中要替换成的节点名称 * @param oRespName 响应消息中被替换的节点名称 * @param tRespName 响应消息中要替换成的节点名称 * @return 响应消息体,此处可为LogoutResponse */ public XMLObject send(XMLObject reqXmlObj, String oReqName, String tReqName, String oRespName, String tRespName) { // 声明返回对象 XMLObject respXmlObj = null; // 替换请求对象的消息节点名称 SamlUtils.renameNode(SamlUtils.asDOMDocument(reqXmlObj), oReqName, tReqName); // 发送SOAP消息,并返回结果 XMLObject tempXmlObj = send(reqXmlObj); // 替换响应对象的消息节点名称 SamlUtils.renameNode(tempXmlObj.getDOM().getOwnerDocument(), oRespName, tRespName); // 构造返回对象,此对象为新建对象 respXmlObj = SamlUtils.fromElement(tempXmlObj.getDOM()); return respXmlObj; } }
接收端
SoapServlet
package xuzs.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; import org.opensaml.ws.soap.soap11.Envelope; import org.opensaml.xml.util.XMLHelper; import org.w3c.dom.Document; import xuzs.common.MessageGen; import xuzs.common.SamlUtils; public class SoapServlet extends HttpServlet { protected static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request.setAttribute("test", new Date()); this.getServletContext().getRequestDispatcher("/index.jsp") .forward(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 首先从request中取出SOAP消息信封 Document reqDom = SamlUtils.parse(request.getInputStream()); // @debug 打印消息体 System.out.println("Receive Message : \n" + XMLHelper.nodeToString(reqDom.getDocumentElement())); // 登出消息 if (reqDom.getElementsByTagName(SamlUtils.LGOUT_REQ).getLength() > 0) { Envelope envelope = (Envelope) SamlUtils.fromElement(reqDom .getDocumentElement()); LogoutRequest logoutRequest = (LogoutRequest) envelope.getBody() .getUnknownXMLObjects().get(0); // @debug 打印处理对象 System.out.println(XMLHelper.nodeToString(SamlUtils .asDOMDocument(logoutRequest).getDocumentElement())); LogoutResponse logoutResponse = MessageGen.generateLogoutResponse(); try { // 信封复用,清除之前的消息体内容 envelope.getBody().getUnknownXMLObjects().clear(); // 将响应消息体放入信封 envelope.getBody().getUnknownXMLObjects().add(logoutResponse); // 设置响应类型为text/xml response.setContentType("text/xml"); PrintWriter out = response.getWriter(); Document respDom = SamlUtils.asDOMDocument(envelope); System.out.println(XMLHelper.nodeToString(respDom.getDocumentElement())); out.print(XMLHelper.nodeToString(respDom.getDocumentElement())); } catch (Exception e) { e.printStackTrace(); } } // 报活消息 else if (reqDom.getElementsByTagName(SamlUtils.ACT_REQ).getLength() > 0) { SamlUtils.renameNode(reqDom, SamlUtils.ACT_REQ, SamlUtils.LGOUT_REQ); Envelope envelope = (Envelope) SamlUtils.fromElement(reqDom .getDocumentElement()); LogoutRequest logoutRequest = (LogoutRequest) envelope.getBody() .getUnknownXMLObjects().get(0); // @debug 打印处理对象 System.out.println(XMLHelper.nodeToString(SamlUtils .asDOMDocument(logoutRequest).getDocumentElement())); LogoutResponse logoutResponse = MessageGen.generateLogoutResponse(); try { // 信封复用,清除之前的消息体内容 envelope.getBody().getUnknownXMLObjects().clear(); // 将响应消息体放入信封 envelope.getBody().getUnknownXMLObjects().add(logoutResponse); // 设置响应类型为text/xml response.setContentType("text/xml"); PrintWriter out = response.getWriter(); Document respDom = SamlUtils.asDOMDocument(envelope); SamlUtils.renameNode(respDom, SamlUtils.LGOUT_RESP, SamlUtils.ACT_RESP); System.out.println(XMLHelper.nodeToString(respDom.getDocumentElement())); out.print(XMLHelper.nodeToString(respDom.getDocumentElement())); } catch (Exception e) { e.printStackTrace(); } } } }
测试类
SendMsgTest
package xuzs.test; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; import org.opensaml.xml.XMLObject; import org.opensaml.xml.util.XMLHelper; import xuzs.common.MessageGen; import xuzs.common.SamlMsgClient; import xuzs.common.SamlUtils; public class SendMsgTest { private static String serverUrl = "http://localhost:8080/opensamlDemo/soapServlet"; public static void main(String[] args){ // 发送报活 sendActivate(); // 发送登出 sendLogout(); // 另一种报活 sendActivate1(); } /** * 发送登出消息 */ public static void sendLogout(){ // 构造客户端 SamlMsgClient client = new SamlMsgClient(serverUrl); // 构造登出请求消息对象 LogoutRequest logoutRequest = MessageGen.generateLogoutRequest(); // @debug 初始消息体,LogoutReuqest System.out.println(XMLHelper.nodeToString(SamlUtils.asDOMDocument(logoutRequest).getDocumentElement())); // 干活 LogoutResponse logoutResponse = (LogoutResponse)client.send(logoutRequest); // @debug 响应消息体,LogoutRespons System.out.println(XMLHelper.nodeToString(SamlUtils.asDOMDocument(logoutResponse).getDocumentElement())); } /** * 发送报活消息1 */ public static void sendActivate1(){ // 构造客户端 SamlMsgClient client = new SamlMsgClient(serverUrl); // 构造登出请求消息对象 LogoutRequest activateRequest = MessageGen.generateLogoutRequest(); // @debug 初始消息体,LogoutReuqest System.out.println(XMLHelper.nodeToString(SamlUtils.asDOMDocument(activateRequest).getDocumentElement())); // 干活 LogoutResponse logoutResponse = (LogoutResponse)client.send(activateRequest, SamlUtils.LGOUT_REQ, SamlUtils.ACT_REQ, SamlUtils.ACT_RESP, SamlUtils.LGOUT_RESP); // @debug 响应消息体,LogoutRespons System.out.println(XMLHelper.nodeToString(SamlUtils.asDOMDocument(logoutResponse).getDocumentElement())); } /** * 发送报活消息 */ public static void sendActivate(){ // 构造客户端 SamlMsgClient client = new SamlMsgClient(serverUrl); // 构造登出请求消息对象(宿主) LogoutRequest activateRequest = MessageGen.generateLogoutRequest(); // @debug 初始消息体,LogoutReuqest System.out.println(XMLHelper.nodeToString(SamlUtils.asDOMDocument(activateRequest).getDocumentElement())); // 修改消息体节点 SamlUtils.renameNode(SamlUtils.asDOMDocument(activateRequest), SamlUtils.LGOUT_REQ, SamlUtils.ACT_REQ); // @debug 发送消息体,ActivateReuqest System.out.println(XMLHelper.nodeToString(activateRequest.getDOM())); // 干活 XMLObject tempXmlObj = client.send(activateRequest); // @debug 响应消息体,ActivateRespons System.out.println(XMLHelper.nodeToString(tempXmlObj.getDOM())); // 替换响应对象的消息节点名称 SamlUtils.renameNode(tempXmlObj.getDOM().getOwnerDocument(), SamlUtils.ACT_RESP, SamlUtils.LGOUT_RESP); // 返回登出响应消息对象(宿主) LogoutResponse activateResponse = (LogoutResponse)SamlUtils.fromElement(tempXmlObj.getDOM()); // @debug 处理消息体,LogoutRespons System.out.println(XMLHelper.nodeToString(activateResponse.getDOM())); } }
MessageGen过于简单,就不贴了。