I want to consume a SOAP Web service which requires an OAuth2 authentication with client_credentials grant type.
From the documentation (http://cxf.apache.org/docs/jax-rs-oauth2.html#JAX-RSOAuth2-AdvancedOAuth2clientapplications) , I have found the BearerAuthSupplier which could be usefull. So I tried
#Bean
public CustomName customName()
{
final JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();
factoryBean.setServiceClass(CustomName.class);
factoryBean.setAddress("");
final CustomName serviceClient = (CustomName ) factoryBean.create();
// Get the underlying Client object from the proxy object of service interface
final org.apache.cxf.endpoint.Client proxy = ClientProxy.getClient(serviceClient);
final HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
final BearerAuthSupplier supplier = new BearerAuthSupplier();
supplier.setAccessTokenServiceUri("");
supplier.setConsumer(new Consumer("client-id", "client-secret"));
supplier.setRefreshEarly(true);
conduit.setAuthSupplier(supplier);
return serviceClient;
}
In the Authorization Header I had a Basic assertion (I wanted a Bearer). Plus, I had no possibility to set the scope of the token. I think there is something I missed ...
To get this working, I had to extend BearerAuthSupplier to somethink like this
public class CustomAuthSupplier extends BearerAuthSupplier {
private String accessTokenServiceUri;
public String getAuthorization(AuthorizationPolicy authPolicy,
URI currentURI,
Message message,
String fullHeader) {
ClientCredentialsGrant clientCredentialsGrant = new ClientCredentialsGrant("scope_needed");
clientCredentialsGrant.setClientId(this.getConsumer().getClientId());
clientCredentialsGrant.setClientSecret(this.getConsumer().getClientSecret());
WebClient wc = WebClient.create(this.accessTokenServiceUri, Collections.singletonList(new OAuthJSONProvider()));
ClientAccessToken at = OAuthClientUtils.getAccessToken(wc,clientCredentialsGrant);
this.setClientAccessToken(at);
return super.getAuthorization(authPolicy, currentURI, message, fullHeader);
}
public void setAccessTokenServiceUri(String uri) {
this.accessTokenServiceUri = uri;
super.setAccessTokenServiceUri(uri);
}
So far it works well, but I find it a bit complicated (and I'm not really sure of what i am doing). My question is : How to perform client credential grant with CXF when calling a Soap WS ?
I am able to call soap service and getting the response from soap client does means connectivity is okay. But the request data values are not mapping to external system request. and we both are using same package and class names.
Please find the code as below:
UserListResponse response = null;
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "UserList")
#ResponsePayload
public UserListResponse UserListRequest(#RequestPayload UserListRequest request) throws Exception {
System.out.println("Enters into UserList()");
try {
//Client call
UserServicesLocator locator = new UserServicesLocator();
UserServicesSoapStub stub = (UserServicesSoapStub) locator.getUserServicesSoap();
response = stub.userList(request);//here the request data values not mapping at external system side
} catch(Exception e) {
e.printStackTrace();
}
return response;
}
Note: Client classes are able to generated using wsdl and in that client classes having pojo structure with serialization and de-sterilization methods.
Can anyone please suggest on this issue.
I would like to add an ExceptionMapper to CXF (2.6.1) which not only communicates the Response code, but also ships the exception in the payload format (I'm using JSON for now).
#Provider
public class CustomExceptionMapper
implements
ExceptionMapper<MyException>
{
...
#Override
public Response toResponse(MyException mex)
{
//I need something here which can convert mex object to JSON and ship it in response
// I want this to be de-serialized on client
//the following returns the status code
return Response.status(Response.Status.BAD_REQUEST).build();
}
...
}
Is there a way to do this ?
You may need to use #Produces to serialize your object to JSON like:
#Produces(MediaType.APPLICATION_JSON)
And then return Response.ok().entity(OBJECT).build();
The way that you can test your service:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(getBaseURI());
ClientResponse response = service.path(ADDRESS).type("application/json").get(ClientResponse.class);
String s = response.getEntity(String.class);
System.out.println(s);
private static URI getBaseURI() {
return UriBuilder.fromUri(SERVER ADDRESS).build();
}
I wrote the following code to implement a Java web service that communicates with an application written in another language on the same host:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
#WebService(name = "MyWebService")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class MyWebService {
#WebMethod(operationName = "methodName", action = "urn:#methodName")
#WebResult(name = "result", partName = "output")
public String methodName(#WebParam(name = "param1", partName = "input") String param1,
#WebParam(name = "param2", partName = "input") String param2){
// ...do something
return "You called this service with params: " + param1 + "," + param2;
}
Since requirements are not to use an application server to expose the web service I instantiated the service from another class as follows:
Endpoint endpoint = Endpoint.create(new MyWebService());
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
Questions:
1) Which is the simplest way to secure this service with username and password considering the architecture of this project?
Any code sample would be greatly appreciated.
2) I made some research and found the use of SOAPHandler and I think it would work for me.
In the case of using the SOAPHandler class how do I add headers to the message to require authentication from the client?
Thank you in advance
thanks so much for the response that's the direction I'm following too but
when I check any of the headers for example:
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
Iterator<SOAPElement> iterator = header.getAllAttributes();
I get a nullpointer exception...any ideas?
I did a working program. Just to add to what you already found out, following is a way to use handler
Endpoint endpoint = Endpoint.create(new MyWebService());
Binding binding = endpoint.getBinding();
List<Handler> handlerChain = new ArrayList<Handler>(1);
handlerChain.add(new MyHandler());
binding.setHandlerChain(handlerChain);
URL url = new URL("http://localhost:7777/MyWebService");
endpoint.publish(url.toString());
MyHandler is class extending Handler interface. Alternately, you can use #HandlerChain annotation which will need an xml configuration file for handlers. Configure this for incoming messages only
public class MyHandler implements SOAPHandler{
#Override
public Set<?> getHeaders() {
// TODO Auto-generated method stub
return null;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public boolean handleFault(MessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean handleMessage(MessageContext context) {
System.out.println("Hehehe the handler");
SOAPMessageContext soapContext = (SOAPMessageContext)context;
try {
SOAPHeader header = soapContext.getMessage().getSOAPPart().getEnvelope().getHeader();
//Check there if the required data (username/password) is present in header or not and return true/false accordingly.
} catch (SOAPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
}
From the client side also, if your client is using JAB-WS, you will have to use client handlers. Following is a typical JAX-WS client invocation example
Dispatch<Source> dispatch = … create a Dispatch<Source>
dispatch.getBinding().setHandlerChain(chain)
Source request = … create a Source object
Source response = dispatch.invoke(request);
Here the handler in chain will add header to outgoing request. Configure this for Outgoing messages only.
What you did is fair enough.
Concerning the authentication you can just expose a method for passing user name and password as login credentials.
Once the user has provided the correct credentials the user has been authenticated.
Note: Now you must maintain session data and make sure that an incoming request is from an authenticated user. The Endpoint just deploys internally a lightweight http server. You must design you web service implementation to keep "state" among requests.
You have 2 more options.
Do the authentication at the SOAP level. I would not really recomend
it. But if you do, note that the Endpoint does not deploy a
WSDL. So you must communicate exactly to the client connecting,
the SOAP header you expect. It is possible though to write a WSDL by
yourself and "attach" it to the Endpoint.
Do the authentication at the http request level. I.e. add a token or
cookie to the http request. To be honest I do not remember if this
is easy using the Endpoint
I am trying to create a standalone client to consume some web services. I must add my username and password to the SOAP Header. I tried adding the credentials as follows:
OTSWebSvcsService service = new OTSWebSvcsService();
OTSWebSvcs port = service.getOTSWebSvcs();
BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
...
When I call a method on the service I get the following exception:
com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5048E: One of "SOAP Header" elements required.
What am I doing wrong? How would I add these properties to the SOAP Header?
Edited: I was using JAX-WS 2.1 included in JDK6. I am now using JAX-WS 2.2. I now get the following exception:
com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC5509E: A security token whose type is [http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken] is required.
How do I go about creating this token?
Data can be transferred in SOAP header (JaxWS) by using #WebParam(header = true):
#WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
#Oneway
public void sendRequest(
#WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
Data message,
#WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
Header serviceHeader);
If you want to generate a client with SOAP Headers, you need to use -XadditionalHeaders:
wsimport -keep -Xnocompile -XadditionalHeaders -Xdebug http://12.34.56.78:8080/TestHeaders/somewsdl?wsdl -d /home/evgeny/DEVELOPMENT/JAVA/gen
If don't need #Oneway web service, you can use Holder:
#WebMethod(operationName = "SendRequest", action = "http://abcd.ru/")
public void sendRequest(
#WebParam(name = "Message", targetNamespace = "http://abcd.ru/", partName = "Message")
Data message,
#WebParam(name = "ServiceHeader", targetNamespace = "http://abcd.ru/", header = true, partName = "ServiceHeader")
Holder<Header> serviceHeader);
Not 100% sure as the question is missing some details but if you are using JAX-WS RI, then have a look at Adding SOAP headers when sending requests:
The portable way of doing this is that
you create a SOAPHandler and mess
with SAAJ, but the RI provides a
better way of doing this.
When you create a proxy or dispatch
object, they implement
BindingProvider interface. When you
use the JAX-WS RI, you can downcast to
WSBindingProvider which defines a
few more methods provided only by the
JAX-WS RI.
This interface lets you set an
arbitrary number of Header object,
each representing a SOAP header. You
can implement it on your own if you
want, but most likely you'd use one of
the factory methods defined on
Headers class to create one.
import com.sun.xml.ws.developer.WSBindingProvider;
HelloPort port = helloService.getHelloPort(); // or something like that...
WSBindingProvider bp = (WSBindingProvider)port;
bp.setOutboundHeader(
// simple string value as a header, like <simpleHeader>stringValue</simpleHeader>
Headers.create(new QName("simpleHeader"),"stringValue"),
// create a header from JAXB object
Headers.create(jaxbContext,myJaxbObject)
);
Update your code accordingly and try again. And if you're not using JAX-WS RI, please update your question and provide more context information.
Update: It appears that the web service you want to call is secured with WS-Security/UsernameTokens. This is a bit different from your initial question. Anyway, to configure your client to send usernames and passwords, I suggest to check the great post Implementing the WS-Security UsernameToken Profile for Metro-based web services (jump to step 4). Using NetBeans for this step might ease things a lot.
I'm adding this answer because none of the others worked for me.
I had to add a Header Handler to the Proxy:
import java.util.Set;
import java.util.TreeSet;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class SOAPHeaderHandler implements SOAPHandler<SOAPMessageContext> {
private final String authenticatedToken;
public SOAPHeaderHandler(String authenticatedToken) {
this.authenticatedToken = authenticatedToken;
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty =
(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
SOAPFactory factory = SOAPFactory.newInstance();
String prefix = "urn";
String uri = "urn:xxxx";
SOAPElement securityElem =
factory.createElement("Element", prefix, uri);
SOAPElement tokenElem =
factory.createElement("Element2", prefix, uri);
tokenElem.addTextNode(authenticatedToken);
securityElem.addChildElement(tokenElem);
SOAPHeader header = envelope.addHeader();
header.addChildElement(securityElem);
} catch (Exception e) {
e.printStackTrace();
}
} else {
// inbound
}
return true;
}
public Set<QName> getHeaders() {
return new TreeSet();
}
public boolean handleFault(SOAPMessageContext context) {
return false;
}
public void close(MessageContext context) {
//
}
}
In the proxy, I just add the Handler:
BindingProvider bp =(BindingProvider)basicHttpBindingAuthentication;
bp.getBinding().getHandlerChain().add(new SOAPHeaderHandler(authenticatedToken));
bp.getBinding().getHandlerChain().add(new SOAPLoggingHandler());
Also, if you're using Maven to build your project, you'll need to add the following dependency:
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>{currentversion}/version>
</dependency>
This provides you with the class com.sun.xml.ws.developer.WSBindingProvider.
Link: https://mvnrepository.com/artifact/com.sun.xml.ws/jaxws-rt
you can add the username and password to the SOAP Header
BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"your end point"));
Map<String, List<String>> headers = new HashMap<String, List<String>>();
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");
prov.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, headers);
Use maven and the plugin jaxws-maven-plugin. this will generate a web service client. Make sure you are setting the xadditionalHeaders to true. This will generate methods with header inputs.
The best option (for my of course) is do it yourserfl. It means you can modify programattly all parts of the SOAP message
Binding binding = prov.getBinding();
List<Handler> handlerChain = binding.getHandlerChain();
handlerChain.add( new ModifyMessageHandler() );
binding.setHandlerChain( handlerChain );
And the ModifyMessageHandler source could be
#Override
public boolean handleMessage( SOAPMessageContext context )
{
SOAPMessage msg = context.getMessage();
try
{
SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
SOAPHeader header = envelope.addHeader();
SOAPElement ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
ele = header.addChildElement( new QName( "http://uri", "name_of_header" ) );
ele.addTextNode( "value_of_header" );
...
I hope this helps you
In jaxws-rt-2.2.10-ources.jar!\com\sun\xml\ws\transport\http\client\HttpTransportPipe.java:
public Packet process(Packet request) {
Map<String, List<String>> userHeaders = (Map<String, List<String>>) request.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS);
if (userHeaders != null) {
reqHeaders.putAll(userHeaders);
So, Map<String, List<String>> from requestContext with key MessageContext.HTTP_REQUEST_HEADERS will be copied to SOAP headers.
Sample of Application Authentication with JAX-WS via headers
BindingProvider.USERNAME_PROPERTY and BindingProvider.PASSWORD_PROPERTY keys are processed special way in HttpTransportPipe.addBasicAuth(), adding standard basic authorization Authorization header.
See also Message Context in JAX-WS
I struggled with all the answers here, starting with Pascal's solution, which is getting harder with the Java compiler not binding against rt.jar by default any more (and using internal classes makes it specific to that runtime implementation).
The answer from edubriguenti brought me close. The way the handler is hooked up in the final bit of code didn't work for me, though - it was never called.
I ended up using a variation of his handler class, but wired it into the javax.xml.ws.Service instance like this:
Service service = Service.create(url, qname);
service.setHandlerResolver(
portInfo -> Collections.singletonList(new SOAPHeaderHandler(handlerArgs))
);
Adding an object to header we use the examples used here,yet i will complete
ObjectFactory objectFactory = new ObjectFactory();
CabeceraCR cabeceraCR =objectFactory.createCabeceraCR();
cabeceraCR.setUsuario("xxxxx");
cabeceraCR.setClave("xxxxx");
With object factory we create the object asked to pass on the header. The to add to the header
WSBindingProvider bp = (WSBindingProvider)wsXXXXXXSoap;
bp.setOutboundHeaders(
// Sets a simple string value as a header
Headers.create(jaxbContext,objectFactory.createCabeceraCR(cabeceraCR))
);
We used the WSBindingProvider to add the header. The object will have some error if used directly so we use the method
objectFactory.createCabeceraCR(cabeceraCR)
This method will create a JAXBElement like this on the object Factory
#XmlElementDecl(namespace = "http://www.creditreport.ec/", name = "CabeceraCR")
public JAXBElement<CabeceraCR> createCabeceraCR(CabeceraCR value) {
return new JAXBElement<CabeceraCR>(_CabeceraCR_QNAME, CabeceraCR.class, null, value);
}
And the jaxbContext we obtained like this:
jaxbContext = (JAXBRIContext) JAXBContext.newInstance(CabeceraCR.class.getPackage().getName());
This will add the object to the header.