在 CXF 3.0 和 Spring 中使用 Contract-First 启用 Police

Enable Police in CXF 3.0 and Spring with Contract-First

我正在尝试使用 CXF 3.1.0 和 Spring 4.1.6

在我的服务中实施 WS-Policy

我发现的大多数示例都是使用 CXF 2 的,并且像 cxf-extension-policy.xml 和 cxf-extension-ws-security.xml 这样的结构在 CXF 新版本中发生了变化。

我试过类似的方法:

package spring;

import java.util.LinkedList;
import java.util.List;

import javax.xml.ws.Endpoint;

import org.apache.cxf.Bus;
import org.apache.cxf.feature.AbstractFeature;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.ws.policy.WSPolicyFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

import com.student.StudentService;
import com.student.service.StudentServiceImpl;

@Configuration
@ImportResource({"classpath:META-INF/cxf/cxf.xml"})
public class CXFConfig {

@Autowired
private Bus cxfBus;

@Bean
public StudentService service(){
    return new StudentServiceImpl();
}

@Autowired
@Bean
public Endpoint serviceImpl(
        final StudentService service){

    EndpointImpl endpoint = new EndpointImpl(this.cxfBus, service);
    endpoint.setAddress("/StudentService");
    endpoint.publish();

    return endpoint;
}
}

我的 WSDL 有一些策略,基本上是来自 this tutorial

的 WSDL

我使用 Contract-first 生成我的 Java 类。

当我 运行 我的项目时,我的政策没有出现。

我试过了

final Map<String, Object> properties = new HashMap<>();
properties.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
properties.put(WSHandlerConstants.PW_CALLBACK_REF, ServerPasswordCallback.class.getName());

EndpointImpl endpoint = new EndpointImpl(this.cxfBus, portType);
endpoint.setProperties(properties);
endpoint.setAddress("/StudentService");
endpoint.publish();

return endpoint;

我也试过了

final Map<String, Object> properties = new HashMap<>();
properties.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
properties.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
properties.put(WSHandlerConstants.PW_CALLBACK_REF, ServerPasswordCallback.class.getName());

cxfBus.getFeatures().add(new WSPolicyFeature());
cxfBus.getInInterceptors().add(new WSS4JInInterceptor(properties));

EndpointImpl endpoint = new EndpointImpl(this.cxfBus, portType);
endpoint.setAddress("/StudentService");
endpoint.publish();

return endpoint;

WSPolicyFeature wsPolicyFeature = new WSPolicyFeature();
wsPolicyFeature.initialize(this.cxfBus);

None 本作品。

有人知道如何使用 Spring 注释配置它吗?

好吧,我的问题不在 CXF/Spring 配置中...在部署时我预期的 WSDL 中。

我期待这样的事情:

<wsdl:binding name="ChangeStudentDetailsImplServiceSoapBinding" type="tns:ChangeStudentDetails">
  <wsp:PolicyReference URI="#ChangeStudentDetailsImplServiceSoapBindingPolicy"/>
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="changeName">
    <soap:operation soapAction="" style="document"/>
    <wsdl:input name="changeName">
      <soap:body use="literal"/>
    </wsdl:input>
    <wsdl:output name="changeNameResponse">
      <soap:body use="literal"/>
    </wsdl:output>
  </wsdl:operation>
</wsdl:binding>

<wsdl:service name="ChangeStudentDetailsImplService">
  <wsdl:port binding="tns:ChangeStudentDetailsImplServiceSoapBinding" name="ChangeStudentDetailsImplPort">
    <wsp:PolicyReference URI="#ChangeStudentDetailsImplServiceSoapBindingPolicy"/>
    <soap:address location="http://localhost:8080/ws-policy/ChangeStudent"/>
  </wsdl:port>
</wsdl:service>

但是,当我使用合同优先方法时,我收到了:

<wsdl:binding name="ChangeStudentDetailsImplServiceSoapBinding" type="tns:ChangeStudentDetails">
  <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="changeName">
    <soap:operation soapAction="" style="document"/>
    <wsdl:input name="changeName">
      <soap:body use="literal"/>
    </wsdl:input>
    <wsdl:output name="changeNameResponse">
      <soap:body use="literal"/>
    </wsdl:output>
  </wsdl:operation>
</wsdl:binding>

<wsdl:service name="ChangeStudentDetailsImplService">
  <wsdl:port binding="tns:ChangeStudentDetailsImplServiceSoapBinding" name="ChangeStudentDetailsImplPort">
    <soap:address location="http://localhost:8080/ws-policy/ChangeStudent"/>
  </wsdl:port>
</wsdl:service>

所以我认为那是行不通的。

我将我的 CXF/Spring 配置更改为

@Autowired
@Bean
public Endpoint serviceImpl(
        final ChangeStudentDetails portType){

    Map<String, Object> inProps = new HashMap<String, Object>();
    inProps.put("action", "UsernameToken");
    inProps.put("passwordType", "PasswordText");
    inProps.put("passwordCallbackClass", ServerPasswordCallback.class.getName());

    EndpointImpl endpoint = new EndpointImpl(this.cxfBus, portType);
    endpoint.getInInterceptors().add(new WSS4JInInterceptor(inProps));
    endpoint.setAddress("/StudentService");
    endpoint.publish();

    return endpoint;
}

当我通过 SOAPUI 执行此操作时,我设置了身份验证类型、用户名和密码并正常工作。我只是不明白为什么我的政策没有出现

一段时间后,我发现WSS4J在契约中使用了拦截器的思想而不是策略。

我将 spring 实施更改为

    EndpointImpl endpoint = new EndpointImpl(this.cxfBus, flightService);
    endpoint.setAddress("/FlightService");
    endpoint.setWsdlLocation("src/main/resources/wsdls/flightservice_v1r1.wsdl");
    endpoint.getProperties().put("ws-security.callback-handler", new ServerPasswordCallback());
    endpoint.publish();

然而,即使在部署我的合同时使用该配置,它也显示 2 wsdl:binding 和 2 wsdl:service...

我找到了解决我问题的另一种方法,而且比第一种方法更少,我将我的策略外化到一个 XML 文件中,并用

注释我的服务
@Policies({
  @Policy(uri="classpath:policies/usernameToken.xml", includeInWSDL=true)
})

我的 Spring 配置被缩减为

EndpointImpl endpoint = new EndpointImpl(this.cxfBus, new FlightServiceImpl());
endpoint.setAddress("/FlightService");
endpoint.getProperties().put("ws-security.callback-handler", new ServerPasswordCallback());
endpoint.publish();

当我部署我的项目时,终于看到了

<wsdl:service name="FlightServiceImplService">
  <wsdl:port binding="tns:FlightServiceImplServiceSoapBinding" name="FlightServiceImplPort">
    <soap:address location="http://localhost:8080/ws-security/FlightService"/>
  </wsdl:port>
  <wsp:PolicyReference URI="#authenticationPolicy"/>
</wsdl:service>

我会尝试这样加载外部策略:

InputStream is = this.getClass().getResourceAsStream(String filePath);
Map<String, Object> requestContext = ((BindingProvider)client).getRequestContext();
PolicyBuilder builder = bus.getExtension(org.apache.cxf.ws.policy.PolicyBuilder.class);
try {
  Policy wsSecuritypolicy = builder.getPolicy(is);
  requestContext.put(PolicyConstants.POLICY_OVERRIDE, wsSecuritypolicy);
}
catch (Exception e) {
  throw new RuntimeException(e.toString());

}

requestContext.put(SecurityConstants.CALLBACK_HANDLER, MyCallBackClass.class.getName());