Announcement Announcement Module
Collapse
No announcement yet.
JaxWsPortProxyFactoryBean and ws-security, please help me out Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JaxWsPortProxyFactoryBean and ws-security, please help me out

    Hi,

    I am developing a web-service client using JaxWsPortProxyFactoryBean.

    The server WSDL mentions that WS-Security username and password are required :

    <s0:Policy s1:Id="Auth.xml">
    <wssp:Identity xmlns:wssp="http://www.bea.com/wls90/security/policy">
    <wssp:SupportedTokens>
    <wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken">
    <wssp:UsePassword Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"/>
    </wssp:SecurityToken>
    </wssp:SupportedTokens>
    </wssp:Identity>
    </s0:Policy>
    <wsp:UsingPolicy s2:Required="true"/>

    then,

    <s5:operation soapAction="http:xxxxxxxx" style="document"/>
    <s2:input>
    <s5:body use="literal"/>
    <wsp:Policy>
    <wsp:PolicyReference URI="#Auth.xml"/>
    </wsp:Policy




    Here is what I did to generate the client :

    1 - I generated the class using wsimport
    2 - I declared my JaxWsPortProxyFactoryBean bean like this :

    <bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPor tProxyFactoryBean">
    <property name="serviceInterface" value=" serviceInterface"/>
    <property name="wsdlDocumentUrl" value="http://wsdlDocumentUrl.wsdl"/>
    <property name="namespaceUri" value="http://namespaceUri"/>
    <property name="serviceName" value="Service_v1"/>
    <property name="portName" value="Service_v1"/>
    <property name="username" value="username" />
    <property name="password" value="password" />
    </bean>

    Unfortunately when I tried to send a request to the server I got kicked out by the server complaining that :
    A web service security fault occurred[{http://schemas.xmlsoap.org/soap/envelope/}Server][No Security header in message but required by policy.]

    I am guessing that my username/password are not in the SOAP header as I expected them to be.

    This gets me wondering :
    1 - Does JaxWsPortProxyFactoryBean support ws-security username/password ?
    2 - If it does, what should I do ?
    3 - If it does not, what alternative do I have ? if any

    I would be really grateful if somebody could help me out as I have no idea how to move on with this.

    Sylvain

  • #2
    After quite a bit of google-digging and a lot of trial and error, I was able to get past this issue.

    First, you'll need to inject a custom handlerResolver into your JaxWsPortProxyFactoryBean.
    You'll also need to create a custom HandlerResolver(javax.xml.ws.handler.HandlerResolv er).

    So your application context will have something like this in it:
    Code:
    <bean id="handlerResolver" class="com.example.MySecurityHandlerResolver"/>
    
    <bean id="sampleService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"
         p:serviceInterface="com.example.SampleSoap"
         p:wsdlDocumentUrl="http://example.com/sample?wsdl"
         p:namespaceUri="http://example.com/sample"
         p:serviceName="SampleService"
         p:portName="SampleSoap"
         p:endpointAddress="http://example.com/myendpoint"
         >
         <property name="handlerResolver" ref="handlerResolver"/>
    </bean>
    Then, its time to create your HandlerResolver implementation.

    First, a basic implementation where you manually build your header. I'm assuming my desired xml will look something like
    Code:
    <Security>
     <UsernameToken>
      <Username>bob</Username>
      <Password>bobspwd</Password>
     </UsernameToken>
    </Security>
    So, the brute force method:

    Code:
    public class MySecurityHandlerResolver implements HandlerResolver {
    
        private final static Logger log = LoggerFactory.getLogger(MySecurityHandlerResolver.class);
    
        @Override
        public List<Handler> getHandlerChain(PortInfo portInfo) {
            List<Handler> handlerList = new ArrayList<Handler>();
            handlerList.add(new SecurityHandler());
            return handlerList;
        }
    
        class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
    
            @Override
            public boolean handleMessage(SOAPMessageContext context) {
                Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
                if (outboundProperty.booleanValue()) {
                    SOAPMessage message = context.getMessage();
                    try {
                        SOAPPart sp = message.getSOAPPart();
                        SOAPEnvelope envelope = sp.getEnvelope();
    
                        SOAPHeader header = envelope.getHeader();
                        if (header==null) {
                            log.info("No header found");
                            header = envelope.addHeader();
                        }
    
                        final String uri="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
                        SOAPFactory factory = SOAPFactory.newInstance();
                        SOAPElement securityElem = factory.createElement("Security","xx", uri);
                        SOAPElement userElem = factory.createElement("UsernameToken","xx",uri);
                        SOAPElement username = factory.createElement("Username","xx",uri);
                        username.setTextContent("Bob");
                        SOAPElement password = factory.createElement("Password","xx",uri);
                        password.setTextContent("bobspwd");
                        userElem.addChildElement(username);
                        userElem.addChildElement(password);
                        securityElem.addChildElement(userElem);
    
                    } catch (Exception e) {
                        log.error("Exception in handler: " + e);
                        return false;
                    }
                } else {
                    // inbound
                }
                return true;
            }
    
            @Override
            public boolean handleFault(SOAPMessageContext context) {
                return true;
            }
    
            @Override
            public void close(MessageContext context) {
                // no-op   
            }
    
            @Override
            public Set<QName> getHeaders() {
                return new TreeSet<QName>();
            }        
        }
    }
    Now, if you're like me, you have a package of generated code for org.oasis_open.docs.wss._2004._01.oasis_200401_wss _wssecurity_secext_1_0 that you really want to use.

    For this, you'll want to marshal your security section into your header....

    Code:
    public class MySecurityHandlerResolver implements HandlerResolver {
    
        private final static Logger log = LoggerFactory.getLogger(MySecurityHandlerResolver.class);
        private static JAXBContext JAXB_CONTEXT;
        
        static {
            try {
                JAXB_CONTEXT = JAXBContext.newInstance(SecuritySoapHeader.class);
            } catch (JAXBException e) {
                log.error("Unable to create the JAXB context", e);
            }
        }
        @Override
        public List<Handler> getHandlerChain(PortInfo portInfo) {
            List<Handler> handlerList = new ArrayList<Handler>();
            handlerList.add(new SecurityHandler());
            return handlerList;
        }
    
        class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
    
            @Override
            public boolean handleMessage(SOAPMessageContext context) {
                Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
                if (outboundProperty.booleanValue()) {
                    final ObjectFactory of = new ObjectFactory();
                    SOAPMessage message = context.getMessage();
                    try {
                        SOAPPart sp = message.getSOAPPart();
                        SOAPEnvelope envelope = sp.getEnvelope();
    
                        SOAPHeader header = envelope.getHeader();
                        if (header==null) {
                            log.info("No header found");
                            header = envelope.addHeader();
                        }
    
                        Marshaller marshaller = JAXB_CONTEXT.createMarshaller();
    
                        // Add the UserNameToken.
                        SecuritySoapHeader secHead = of.createSecuritySoapHeader();
                        UsernameToken token = of.createUsernameToken();
                        token.setUsername("Bob");
                        token.setPassword(bobspwd");
                        secHead.setUsernameToken(token);
                        JAXBElement<SecuritySoapHeader> sec = of.createSecurity(secHead);
                        marshaller.marshal(sec, header);                    
    
                    } catch (Exception e) {
                        log.error("Exception in handler: " + e);
                        return false;
                    }
                } else {
                    // inbound
                }
                return true;
            }
    
            @Override
            public boolean handleFault(SOAPMessageContext context) {
                return true;
            }
    
            @Override
            public void close(MessageContext context) {
                // no-op   
            }
    
            @Override
            public Set<QName> getHeaders() {
                return new TreeSet<QName>();
            }        
        }
    }
    There you go, that should do it.

    Comment


    • #3
      ws security

      Below code works for me


      @Override
      public boolean handleMessage(SOAPMessageContext context) {
      Boolean outbound = (Boolean)
      context.get(MessageContext.MESSAGE_OUTBOUND_PROPER TY);
      if (outbound) {
      try{
      SOAPMessage msg=context.getMessage();
      SOAPPart sp = msg.getSOAPPart();
      SOAPEnvelope envelope = sp.getEnvelope();
      SOAPHeader header = envelope.getHeader();
      if (header==null) {
      log.info("No header found");
      header = envelope.addHeader();
      }
      WSSecHeader wsheader = new WSSecHeader();
      // wsheader.setMustUnderstand(false);
      wsheader.insertSecurityHeader(sp);
      WSSecUsernameToken token = new WSSecUsernameToken();
      token.setPasswordType(WSConstants.PASSWORD_TEXT);
      token.setUserInfo("myuser", "mypass");
      token.build(sp, wsheader);


      }catch(Exception e){
      e.printStackTrace();
      }
      }
      return true;
      }

      Comment


      • #4
        hi,

        Thanks for your replies.

        I also sorted this out a couple of months ago. Here is what I did, in case someone is also stuck with this problem.

        Code:
        <bean id="proxyFactory" 
            class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
            <property name="serviceClass" value="com.xxx.zzz.ws.ServiceV10"/>
            <property name="address">
           	 	<value>http://serverurl:8080?WSDL</value> 
            </property>
            <property name="outInterceptors">  
                        <list>  
                            <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />    
                            <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />    
                            <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">    
                                <constructor-arg>    
                                    <map>    
                                        <entry key="action" value="UsernameToken" />    
                                        <entry key="passwordType"    
                                            value="PasswordText" />    
                                        <entry key="user" value="cxfClient" />    
                                        <entry key="passwordCallbackRef">    
                                            <ref bean="clientPasswordCallback" />    
                                        </entry>    
                                    </map>    
                                </constructor-arg>    
                            </bean>    
                        </list>
        	</property>
        	<property name="inInterceptors">  
        		<list>  
                            <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
                   </list>         
        	</property>
        
        </bean>
        Code:
        <bean id="clientPasswordCallback" class="com.xxx.zzz.ws.clientPasswordCallback">
        	<property name="configFilePath">
           	 	  	<value>D:/Conf/parameters.properties</value>
                 </property>
        
        </bean>
        The code of the class com.xxx.zzz.ws.clientPasswordCallback is (it reads the password/identifier from a property file) :

        Code:
        public class clientPasswordCallback implements CallbackHandler {
        	
        	private final static Logger LOGGER = Logger.getLogger(clientPasswordCallback.class);
        	private String configFilePath;
        	
        	public void setConfigFilePath(String configFilePath) {
        		this.configFilePath = configFilePath;
        	}
        	
        	public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        		
        		Properties properties = new Properties();
        		try {
        		    properties.load(new FileInputStream(configFilePath));
        		} catch (IOException e) {
        			
        			LOGGER.error(e.getMessage(),e);
        			return;
        		}
        		
        		
        		for(int i=0;i<callbacks.length;i++){
        			WSPasswordCallback ps=(WSPasswordCallback) callbacks[i];
        		    ps.setPassword(properties.getProperty("webservice.password"));  
                    ps.setIdentifier(properties.getProperty("webservice.identifier"));  
        		}
        	}
        }

        Hope it will help someone...

        Sylvain

        Comment

        Working...
        X