javaspring-securitycxfws-security

Custom interceptor for cxf service to allow spring security authentication


I'm using spring security for authentication in my app and now I'm exposing a web service using cxf and I want to authenticate the current web service call using spring security. But I need to access the certificate in the web service call (i'm using ws-security)

This is an excerpt of my cxf bean declaration:

<jaxws:endpoint id="service2"
                    implementor="xxx.service2.CXFLibraryImpl"
                    wsdlLocation="classpath:service2.wsdl"
                    address="/service2">
  <jaxws:inInterceptors>

            <bean class="com.kprtech.service.ws.service2.MyWSS4JInInterceptor">
            </bean>
</jaxws:endpoint>

THis is the wsdl exposing ws-security:

<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions name="CXFLibraryImplService" targetNamespace="http://service2.ws.service.kprtech.com/" xmlns:ns1="http://cxf.apache.org/bindings/xformat" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://service2.ws.service.kprtech.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://service2.ws.service.kprtech.com/" version="1.0" xmlns:ns1="http://cxf.apache.org/bindings/xformat" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://service2.ws.service.kprtech.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xs:element name="sayHello" type="tns:sayHello" />
<xs:element name="sayHelloResponse" type="tns:sayHelloResponse" />
<xs:complexType name="sayHello">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="sayHelloResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
  </wsdl:types>
  <wsdl:message name="sayHello">
    <wsdl:part element="tns:sayHello" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="sayHelloResponse">
    <wsdl:part element="tns:sayHelloResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="Library">
    <wsdl:operation name="sayHello">
      <wsdl:input message="tns:sayHello" name="sayHello">
    </wsdl:input>
      <wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="CXFLibraryImplServiceSoapBinding" type="tns:Library">
    <wsp:PolicyReference URI="#SignEncr" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" />
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="sayHello">
      <soap:operation soapAction="" style="document" />
      <wsdl:input name="sayHello">
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output name="sayHelloResponse">
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CXFLibraryImplService">
    <wsdl:port binding="tns:CXFLibraryImplServiceSoapBinding" name="CXFLibraryImplPort">
      <soap:address location="http://localhost:8888/domicilios/services/service2" />
    </wsdl:port>
  </wsdl:service>
    <wsp:Policy wsu:Id="SignEncr" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">

    <wsp:ExactlyOne>
      <wsp:All>
        <sp:AsymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <wsp:Policy>
            <sp:InitiatorToken>
              <wsp:Policy>
                <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
                  <wsp:Policy>
                    <sp:RequireThumbprintReference />
                      <sp:WssX509V1Token10 />
                  </wsp:Policy>
                </sp:X509Token>
              </wsp:Policy>
            </sp:InitiatorToken>
            <sp:RecipientToken>
              <wsp:Policy>
                <sp:X509Token sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
                  <wsp:Policy>
                    <sp:RequireThumbprintReference />
                        <sp:WssX509V3Token10 />
                  </wsp:Policy>
                </sp:X509Token>
              </wsp:Policy>
            </sp:RecipientToken>
            <sp:AlgorithmSuite>
              <wsp:Policy>
                <sp:TripleDesRsa15 />
              </wsp:Policy>
            </sp:AlgorithmSuite>
            <sp:Layout>
              <wsp:Policy>
                <sp:Strict />
              </wsp:Policy>
            </sp:Layout>
            <sp:IncludeTimestamp />
            <sp:OnlySignEntireHeadersAndBody />
          </wsp:Policy>
        </sp:AsymmetricBinding>
          <sp:Wss10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
                <wsp:Policy>
                    <sp:MustSupportRefKeyIdentifier />
                    <sp:MustSupportRefIssuerSerial />
                </wsp:Policy>
            </sp:Wss10>
        <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <sp:Body />
        </sp:SignedParts>

      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>
</wsdl:definitions>

The problem is that even with my custom interceptor cxf is using org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JInInterceptor

and trying to validate certs and security tokens against a crypto, and I want to do this operation by my own in my custom interceptor com.kprtech.service.ws.service2.MyWSS4JInInterceptor


Solution

  • I solved this some time ago:

    You need a CXF configuration like the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jaxws="http://cxf.apache.org/jaxws"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
    
    
        <import resource="classpath:META-INF/cxf/cxf.xml"/>
        <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
        <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
        <import resource="classpath:META-INF/cxf/cxf-extension-policy.xml"/>
        <import resource="classpath:META-INF/cxf/cxf-extension-ws-security.xml"/>
    
        <bean id="implementorBean" class="ws.Implementor"/>
    
        <jaxws:endpoint id="implementor"
                        implementor="#implementorBean"
                        wsdlLocation="/ws/server/service.wsdl"
                        address="/service">
    
            <jaxws:inInterceptors>
    
                <bean class="ws.SpringSecurityInterceptor">
    
                </bean>
            </jaxws:inInterceptors>
    
            <jaxws:properties>
                <entry key="ws-security.signature.properties" value="/ws/security/server-crypto.properties"/>
                <entry key="ws-security.signature.username" value="serverkey"/>
                <entry key="ws-security.callback-handler" value="ws.ServerCallback"/>
            </jaxws:properties>
    
        </jaxws:endpoint>
    
    </beans>
    

    And implement the interceptor like this one:

    public class SpringSecurityInterceptor extends AbstractWSS4JInterceptor {
    
        private ThreadLocal<Subject> currentSubject = new ThreadLocal<Subject>();
    
    
        public SpringSecurityInterceptor() {
            super();
            setPhase(Phase.PRE_PROTOCOL);
            getAfter().add(WSS4JInInterceptor.class.getName());
            getAfter().add(PolicyBasedWSS4JInInterceptor.class.getName());
        }
    
    
        public void handleMessage(SoapMessage message) throws Fault {
    
            try {
    
                Subject subject = (Subject) currentSubject.get();
    
                if (subject == null) {
                    subject = new Subject();
                    currentSubject.set(subject);
                }
                List<Object> results = (List<Object>)message.get(WSHandlerConstants.RECV_RESULTS);
                if (results == null) {
                    return;
                }
                for (Iterator iter = results.iterator(); iter.hasNext();) {
                    WSHandlerResult hr = (WSHandlerResult) iter.next();
                    if (hr == null || hr.getResults() == null) {
                        return;
                    }
    
                        for (Iterator it = hr.getResults().iterator(); it.hasNext();) {
                          WSSecurityEngineResult er = (WSSecurityEngineResult) it.next();
    
                            if (er != null && er.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE) instanceof X509Certificate) {
                              X509Certificate cert = (X509Certificate)er.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
    
                                  // TODO do something with the certificate
    
                          }
                        }
    
                }
    
                message.put(Subject.class, subject);
    
            } catch (java.lang.reflect.UndeclaredThrowableException e) {
                Throwable undeclared = e.getUndeclaredThrowable();
                if (undeclared != null
                        && undeclared instanceof java.lang.reflect.InvocationTargetException) {
                    throw new Fault(
                            ((java.lang.reflect.InvocationTargetException) undeclared)
                                    .getTargetException());
                }
    
            } finally {
                currentSubject.set(null);
            }
        }
    
    
    }