Announcement Announcement Module
Collapse
No announcement yet.
JaxRpcPortProxyFactoryBean with WSS header based security Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JaxRpcPortProxyFactoryBean with WSS header based security

    (any intellij-firefox users get bitten by ctrl-w closing windows in firefox? This is literally the 3rd time I try to write this post. Need to figure out how to change that key binding)

    So I'm having some issue getting wss authentication working from a client using JaxPortProxyFactoryBean. Most of the forum posts and search results seem to indicate that setting the username and password fields in the FactoryBean would solve the problem but that doesn't seem to be the case. I get:

    Code:
    nested exception is WSDoAllReceiver: Request does not contain required Security header
    when I try invoking an authenticated service and I've checked the state of the class and verified correct setting of the username and password fields before the delegated call.invoke to the axis call implementation.

    the midtier is expecting something in this form:
    Code:
            <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
                <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-7691763">
                    <wsu:Created>2008-03-20T20:29:38.718Z</wsu:Created>
                    <wsu:Expires>2008-03-20T20:34:38.718Z</wsu:Expires>
                </wsu:Timestamp>
                <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-1364036">
                    <wsse:Username>testuser</wsse:Username>
                    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">7f84a92e74c0bdc4d4ea51e3a91ede42</wsse:Password>
                </wsse:UsernameToken>
            </wsse:Security>
    but sniffing the soap post it's clear that no header is being sent at all. I've tried coding a handler myself and registering it into the service proxy with a post processor. Which works, but I seem to be having trouble manually building a security header that would satisfy my web service.

    Code:
    public void handleRequest(...
    
                    SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
                    SOAPHeader header = envelope.getHeader();
                    SOAPElement securityHeader = header.addChildElement("wsse:Security", "wsse", securityHeaderUri);
                    SOAPElement usernameToken = securityHeader.addChildElement("wsse:UsernameToken", "wsu", usernameTokenUri);
                    usernameToken.addChildElement("wsse:Username").addTextNode(user.getUsername());
                    SOAPElement passwordToken = usernameToken.addChildElement("wsse:Passord");
                    passwordToken.addAttribute(envelope.createName("Type"), passwordTextUri);
                    passwordToken.addTextNode(user.getSessionToken());
    I'm guessing the lack of wsu:Id doesn't help, and I'm not exactly sure how I would go about building the correct timestamp programmatically. In fact finding information on how these things should be put together is tricky.

    So I'm thinking there must be an easier way. Surely most people need to use WSS authentication when they use this proxy factory bean. So does anybody have any pointers on how to get this working? I've spent way too long on what seems like such a trivial thing as putting credentials into a soap header.
    Last edited by elchuppa; Mar 22nd, 2008, 02:44 PM.

  • #2
    You could try using Apache WSS4J, which offers a JAX-RPC handler for doing WS-Security.

    Comment


    • #3
      thanks arjen. I just came across wss4j yesterday and ended up manually creating the security header with it. The handler sounds cool but I wasn't quite sure how that could be hooked into the spring jaxrpcproxy. I ended up doing the following:

      Code:
      import test.User;
      import org.acegisecurity.Authentication;
      import org.acegisecurity.context.SecurityContextHolder;
      import org.apache.axis.message.SOAPEnvelope;
      import org.apache.axis.soap.MessageFactoryImpl;
      import org.apache.ws.security.WSConstants;
      import org.apache.ws.security.message.WSSecHeader;
      import org.apache.ws.security.message.WSSecTimestamp;
      import org.apache.ws.security.message.WSSecUsernameToken;
      import org.apache.xml.security.c14n.Canonicalizer;
      import org.w3c.dom.Document;
      
      import javax.xml.namespace.QName;
      import javax.xml.rpc.JAXRPCException;
      import javax.xml.rpc.handler.GenericHandler;
      import javax.xml.rpc.handler.MessageContext;
      import javax.xml.rpc.handler.soap.SOAPMessageContext;
      import javax.xml.soap.MessageFactory;
      import javax.xml.soap.SOAPException;
      import javax.xml.soap.SOAPMessage;
      import javax.xml.soap.SOAPPart;
      import java.io.ByteArrayInputStream;
      
      
      public class AuthenticationHandler extends GenericHandler {
      
      
          public QName[] getHeaders() {
              return null;
          }
      
          public boolean handleRequest(MessageContext context) {
              final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
              if (authentication != null) {
                  User user = (Usere) authentication.getPrincipal();
                  SOAPMessageContext smc = (SOAPMessageContext) context;
                  SOAPMessage msg = smc.getMessage();
                  WSSecUsernameToken usernameToken = new WSSecUsernameToken();
                  WSSecTimestamp timestamp = new WSSecTimestamp();
                  try {
                      SOAPPart soapPart = msg.getSOAPPart();
                      SOAPEnvelope env = (SOAPEnvelope) soapPart.getEnvelope();
                      Document doc = env.getAsDocument();
      
                      usernameToken.setPasswordType(WSConstants.PASSWORD_TEXT);
                      usernameToken.setUserInfo(user.getUsername(), user.getSessionToken());
                      usernameToken.prepare(doc);
      
                      timestamp.prepare(doc);
      
                      WSSecHeader securityHeader = new WSSecHeader();
                      securityHeader.insertSecurityHeader(doc);
                      usernameToken.prependToHeader(securityHeader);
                      timestamp.prependToHeader(securityHeader);
                      SOAPMessage authMessage = toSOAPMessage(doc);
                      smc.setMessage(authMessage);
      
                  }
                  catch (SOAPException ex) {
                      throw new JAXRPCException(ex);
                  } catch (Exception e) {
                      throw new RuntimeException(e);
                  }
              }
              return true;
      
      
          }
      
          public SOAPMessage toSOAPMessage(Document doc) throws Exception {
              Canonicalizer c14n =
                      Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
              byte[] canonicalMessage = c14n.canonicalizeSubtree(doc);
              ByteArrayInputStream in = new ByteArrayInputStream(canonicalMessage);
              MessageFactory factory = new MessageFactoryImpl();
              return factory.createMessage(null, in);
          }
      }
      This appears to work which is a hell of a relief. Although it is a pain to have to create a postprocessing bean for each service since I have to configure each one with the appropriate namespaceuri and portname. Still thinking about how to reference the services properties from the PostProcessor context. I imagine if I had used the handler I wouldn't have to figure that out...

      Comment

      Working...
      X