opensaml的SOAP消息发送和接收

首先是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过于简单,就不贴了。

posted @ 2013-02-04 01:31  许仙儿  阅读(1700)  评论(2编辑  收藏  举报