Intercepting cxf web service header with Apache Camel (Java DSL) - apache-camel

I created a web service client to handle cxf soap web services with apache camel.
String serviceUri = "cxf:http://localhost:10000/myservice?serviceClass=" +
MyRequest.class.getCanonicalName();
from(uri).to("mock:xyz");
The web service receives the soap call but throws an exception since the request requires a handling for wss.
org.apache.cxf.binding.soap.SoapFault: MustUnderstand headers: [{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood.
The reason is, that the service requires ws security, which can be seen by lloking at the request.
<SOAP-ENV:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" SOAP-ENV:mustUnderstand="1">
I found out that I need to implement an interceptor to handle header properties.
My questions:
How can I add an interceptor to handle the header attributes with Camel Java-DSL?
Will this be sufficient to get rid of the SOAP Fault?

You can do it through
cxfEndpointConfigurer option #see: Camel-CXF configuration
(I use Spring (it is much easier)), but I guess for DSL URI will look like:
String serviceUri = "cxf:http://localhost:10000/myservice?serviceClass=" +
MyRequest.class.getCanonicalName() +
"&cxfEndpointConfigurer="+ MyConfigurer.class.getCanonicalName();
by implementing org.apache.camel.component.cxf.CxfEndpointConfigurer you have ability to add an Interceptor inside configureServer method
server.getEndpoint().getInInterceptors().add(new MyJAASLoginInterceptor());
if you run your Camel in container with JAAS (like JBOSS) you can use extension from
org.apache.cxf.interceptor.security.JAASLoginInterceptor
with needed callback handler.
Simple example which validates user/password from WSS header against JBOSS users:
public class MyJAASLoginInterceptor extends javax.security.auth.callback.JAASLoginInterceptor {
#Override
protected CallbackHandler getCallbackHandler(String name, String password) {
return new org.apache.cxf.interceptor.security.NamePasswordCallbackHandler(name, password, "setCredential");
}
}

Related

Restful API becomes 404 when using the CXF at the same time

I have a project starts up with Spring Boot.
It has some restful API via Spring Integration inbound gateway.
Afterward, some webservice endpoint added to the project with CXF.
When I setup the CXFServlet mapping, all the restful API became 404.
Only I suspend the CXF config the restful API available again.
May I know if there is anything block the restful API or the spring integration inbound gateway during using CXF?
CXFServlet and Bus
#Configuration
#ComponentScan("com.kennie")
#ImportResource("classpath:cxf-services.xml")
public class SimbaAdapterApplicationConfiguration {
#Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
#Bean(name=Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus bus = new SpringBus();
bus.getInInterceptors().add(new LoggingInInterceptor());
bus.getOutInterceptors().add(new LoggingOutInterceptor());
return bus;
}
XML configuration
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<jaxws:server id="MyService" address="/ws/MyService"
serviceClass="com.kennie.IMyService" >
<jaxws:serviceBean>
<ref bean="myServiceImpl" />
</jaxws:serviceBean>
</jaxws:server>
Service Interface
#WebService
public interface IMyService{
#WebMethod
public #WebResult(name = "Response") Response doRequest(
#WebParam(name = "Request", mode = WebParam.Mode.IN)
Request request
);
}
I'm not familiar with CXF, but I know that Spring Integration HTTP is fully based on Spring MVC. So, if you can configure Spring MVC over CXF, all those Spring Integration HTTP Inbound Gateways will be available there as well.
I think your problem is somewhere with distinguishing Servlet mapping.
Looks like your REST API is routed through the CXF Servlet and that one doesn't like it, hence rejecting.
When you add CXF to your code, all the RESTful APIs will be routed through it.
I see two contradictory settings with the way you have configured CXF -
The url-mapping . You are injecting CXF with this code:
#Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
Meaning the url at which CXF is listening is /ws/*.
The jax-ws server! Firstly, you need to change it to jax-rs . WS is for SOAP. RS is for Restful. You have defined its address as:
<jaxws:server id="MyService" address="/ws/MyService"
Meaning the server is listening at /ws/MyService
CXF and the jax-rs server are both listening at ws/ something. Now, this is not really a problem. You just need to add this to the URL you are hitting so that the complete URL is something like this:
http:<server>:<port>/<context-root>/<CXF Endpoint>/<jax-rs server address>/<REST API endpoint>
I am guessing you don't want ws appearing twice in the URL. Remove it from the jax-rs address.
Hope this helps.
You can register more one servlet for http rest api, this method is tested and OK:
#SpringBootApplication(
//scanBasePackages = {"com.huawei.manage"}
)
public class Application {
#Bean
public ServletRegistrationBean dispatcherServlet() {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.scan("com.huawei.manage.hsf.controller");
DispatcherServlet rest_dispatcherServlet = new DispatcherServlet(applicationContext);
ServletRegistrationBean registrationBean = new ServletRegistrationBean(rest_dispatcherServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/*");
return registrationBean;

Http Connection Pooling in Camel

I am using Camel as an Orchestration Engine.
clients sends HTTP request <-> CAMEL code <---- HTTP Req----- > external
server(s)
I am using HTTP4 Component (with default settings) for making HTTP Requests
to external server. I have quite a few http backends.
Right now the way we are making http calls to our backend is as follow:-
// The producer is created during app initialisation. This is actually done
via blueprint.xml
ProducerTemplate producer = camelContext.createProducerTemplate();
// Whenever I need to make a http call I am executing the below code with
URL set as something like:- "http4://order-api:8099/orders/v1/ordersearch/"
Exchange exchange = producer.request(URL, new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
log.info("Executing the HTTP request : URL - " + URL + " Headers -
" + headers + " Body : " + body);
exchange.getIn().setHeaders(headers);
exchange.getIn().setBody(body);
}
});
The query I am having is:-
Does HTTP4 in the default setting camel uses some http connection
pooling while making a call to the external servers?
If yes Is there a way I can configure the connection pooling from
blueprint.xml?
I am using Camel 2.16.1 and the application is deployed in Karaf 3.0.5.
The http4 component use Apache HttpClient, which support pooling with the use of a HttpClientConnectionManager.
By default, camel uses a PoolingHttpClientConnectionManager which is configured with the properties connectionsPerRoute and maxTotalConnections.
If you want to have more control over this clientConnectionManager, you can provide your own implementation of org.apache.http.conn.HttpClientConnectionManager
See HttpClient connection manager

Restlet CorsFilter with ChallengeAuthenticator

I'm building a RESTful API with the Restlet framework and need it to work with cross domain calls (CORS) as well as basic authentication.
At the moment I'm using the CorsFilter which does the job of making my webservice support CORS requests. But, when I try to use this with a simple ChallengeAuthenticator with HTTP Basic Authentication it won't work as I want it to (from a web site).
When I access the webservice directly via Chrome it works as intended, but when I try it in a small web application written in angularjs (jquery/javascript) and try to access the webservice it does not.
Basically what happens is that when a OPTIONS request is sent to my webservice it will not respond with the headers: 'Access-Control-Allow-Origin', 'Access-Control-Allow-Credentials', etc. as it should. Instead it is sending a respond with HTTP status code 401 saying that the authentication failed.. Is this because the authenticator is overriding the CorsFilter somehow?
My createInboundRoot method can be seen below.
#Override
public Restlet createInboundRoot() {
ChallengeAuthenticator authenticator = createAuthenticator();
RoleAuthorizer authorizer = createRoleAuthorizer();
Router router = new Router(getContext());
router.attach("/items", ItemsServerResource.class);
router.attach("/items/", ItemsServerResource.class);
Router baseRouter = new Router(getContext());
authorizer.setNext(ItemServerResource.class);
authenticator.setNext(baseRouter);
baseRouter.attach("/items/{itemID}", authorizer);
baseRouter.attach("", router);
// router.attach("/items/{itemID}", ItemServerResource.class);
CorsFilter corsFilter = new CorsFilter(getContext());
corsFilter.setNext(authenticator);
corsFilter.setAllowedOrigins(new HashSet(Arrays.asList("*")));
corsFilter.setAllowedCredentials(true);
return corsFilter;
}
(The authorizer and authenticator code is taken from the "official" restlet guide for authorization and authentication)
I've tried alot of changes to my code but none which given me any luck. But I noticed that when setting the argument "optional" in ChallengeAuthenticator to true (which "Indicates if the authentication success is optional") the CorsFilter does its job, but obviously the ChallengeAuthenticator does not care about authenticating the client and lets anything use the protected resources..
Has anyone had a similar problem? Or have you solved this (CORS + Authentication in Restlet) in any other way?
Thanks in advance!
I think that it's a bug of the Restlet CORS filter. As a matter of fact, the filter uses the method afterHandle to set the CORS headers. See the source code: https://github.com/restlet/restlet-framework-java/blob/4e8f0414b4f5ea733fcc30dd19944fd1e104bf74/modules/org.restlet/src/org/restlet/engine/application/CorsFilter.java#L119.
This means that the CORS processing is done after executing the whole processing chain (authentication, ...). So if your authentication failed, you will have a status code 401. It's actually the case since CORS preflighted requests don't send authentication hints.
For more details about using CORS with Restlet, you could have a look at this link: https://templth.wordpress.com/2014/11/12/understanding-and-using-cors/. This can provide you a workaround until this bug was fixed in Restlet itself.
I opened an issue in Github for your problem: https://github.com/restlet/restlet-framework-java/issues/1019.
Hope it helps,
Thierry
The CorsService (in 2.3.1 coming tomorrow) contains also a skippingResourceForCorsOptions property, that answers directly the Options request without transmitting the request to the underlying filters and server resources.

Invoking a rest service passing dynamic key value parameters using cxf-rs components

I have to create a Fuse service which would in-turn invoke a REST service exposed by an external service provider. Fuse service will be receiving request in XML format and converting to a query string before invoking the REST service.
Sample request XML for Fuse service -
<CustomerDetails>
<CustomerName>ABC</CustomerName>
<CustomerAge>28</CustomerAge>
<CustomerName>DEF</CustomerName>
<CustomerAge>54</CustomerAge>
<CustomerDetails>
The REST service consumes request in key value params and responds back in XML format.
sample URL:
https://www.customer.com/cust/api/v1/store/abc.xml?Customername=ABC&Customerage=28&Customername=DEF&customerage=54)
I have tried searching a lot but couldn't find any tutorial in the net.
Can someone please provide suggestions on how to implement the fuse service using cxf-rs components (preferably Spring DSL camel configuration )
Thanks in advance..
If you just want to turn the XML request to the url parameter, you can just use jaxb data format to unmarshal the request and use a bean object to setup the URI request parameters. You don't need to use camel-cxf component.
from("direct:start").unmarshal(jaxb).process(new Processor() {
public void process(Exchange exchange) throws Exception {
// get the request object
CustomerDetail request = exchange.getIn().getBody();
// Just mapping the request object into a query parameters.
String query = requestToParameter(request);
exchange.getIn().setHeader(Exchange.HTTP_QUERY, query);
// to remove the body, so http endpoint can send the request with Get Method
exchange.getIn().setBody(null);
}).to("https://www.customer.com/cust/api/v1/store/abc.xml");

Why does Apache CXF WS-Security implementation ignore GET requests

I'm exposing a service as a SOAP Webservice using Apache CXF 2.4.1.
I'm using CXF's WS-Security implementation to implement a custom authentication and authorization.
Everything is being wired with Spring.
My CallbackHandler is being properly called upon a normal SOAP request.
But when a request comes from a simple GET request, like calling the service from a browser with the url below, my handler is not called.
http://localhost:8080/ws/customerService/findById?id=1
Checking CXF's WS-Security code it is ignoring GET requests completely (code below).
Why?
public void handleMessage(SoapMessage msg) throws Fault {
if (msg.containsKey(SECURITY_PROCESSED) || isGET(msg)) {
return;
}
...
}

Resources