Results 1 to 4 of 4

Thread: JaxWsPortProxyFactoryBean and ws-security, please help me out

  1. #1
    Join Date
    Nov 2011
    Posts
    8

    Default 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. #2
    Join Date
    Jun 2010
    Posts
    9

    Default

    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.

  3. #3
    Join Date
    Aug 2012
    Posts
    1

    Default 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;
    }

  4. #4
    Join Date
    Nov 2011
    Posts
    8

    Default

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •