Client side authentication with jax-rs and cxf - java

We are going to switch JAX-RS implementation from Jersey to Apache CXF 3.0. I just can't figure out how basic authentication is done the Apache CXF way. All examples I found where around CXF WebClient, not the JAX-RS Client API.
This is what's working with Jersey:
Client client = ClientBuilder.newClient();
client.register(HttpAuthenticationFeature.basic(config.getUsername(),config.getPassword()));
How can this be done with Apache CXF?

Create a ClientRequestFilter to perform the basic authentication:
#Provider
public class Authenticator implements ClientRequestFilter {
private String user;
private String password;
public Authenticator(String user, String password) {
this.user = user;
this.password = password;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(
HttpHeaders.AUTHORIZATION, getBasicAuthentication());
}
private String getBasicAuthentication() {
String userAndPassword = this.user + ":" + this.password;
byte[] userAndPasswordBytes = userAndPassword.getBytes("UTF-8");
return "Basic " + Base64.getEncoder().encodeToString(userAndPasswordBytes);
}
}
And register it in your Client:
Client client = ClientBuilder.newClient().register(new Authenticator(user, password));
The solution above uses the Java 8 Base64.Encoder to perform the Base64 encoding.
If, for some reason, you are not using Java 8, you can use BaseEncoding from Google Guava.

I've opened an improvement in CXF Jira for this: https://issues.apache.org/jira/browse/CXF-6817

Related

Java client for REST web service with Netbeans

I am trying to write a Java client for a REST web service, defined this way:
#Path("/")
public class Translator {
public Translator() { }
#POST
#Produces("application/json")
#Path("/translate")
public String translate(#QueryParam("dir") String dir, #QueryParam("string")String string, #QueryParam("user")String user, #QueryParam("key")String key){
return doTranslation(dir, string, user, key);
}
}
I have tried to use the NetBeans option "New Restful Java client", selecting for the REST source the project that contains the webservice.
But it generates a class with one method that does not have parameters:
public class NewJerseyClient {
private WebTarget webTarget;
private Client client;
private static final String BASE_URI = "http://localhost:8086/TranslatorREST/Translator";
public NewJerseyClient() {
client = javax.ws.rs.client.ClientBuilder.newClient();
webTarget = client.target(BASE_URI);
}
public String translate() throws ClientErrorException {
return webTarget.path("translate").request().post(null, String.class);
}
}
So I don't see a way to pass parameters to the web service.
I can succesfully use this web service from SoapUI, provided that I don't enable the option "Post QueryString", in which case the web service receives "null" for all the parameters.
Thanks in advance.
You can add the parameters with .queryParam() :
public class NewJerseyClient {
private WebTarget webTarget;
private Client client;
private static final String BASE_URI = "http://localhost:8086/TranslatorREST/Translator";
public NewJerseyClient() {
client = javax.ws.rs.client.ClientBuilder.newClient();
webTarget = client.target(BASE_URI);
}
public String translate() throws ClientErrorException {
return webTarget.path("translate").queryParam("dir", "myDir")
.queryParam("string", "myString")
.queryParam("user", "myUser")
.queryParam("key", "myKey").request().post(null, String.class);
}
}

Configuring Authentication Header in Jersey API Client Side

There is following way to configure the authentication header in Jersey API .
//Universal builder having different credentials for different schemes
HttpAuthenticationFeature feature = HttpAuthenticationFeature.universalBuilder()
.credentialsForBasic("username1", "password1")
.credentials("username2", "password2").build();
final Client client = ClientBuilder.newClient();
client.register(feature);
But not able to figure out how to pass extra parameter to authentication header for e.g. IntegatorKey, SendBehalfOf. those are specific REST service call.
In My Case to call REST service need to pass following parameter as part of authentication header.
Username
Password
IntegatorKey
SendBehalfOf
How should I achieve this using the Jersey API ?
You didn't provide enough information in your question. It's hard guessing what you are trying to achieve. You really should consider updating your question with more details.
Having a look at the superficial information you provided, I guess you are trying to access the DocuSign REST API. If so, you could create a ClientRequestFilter, as following:
public class DocuSignAuthenticator implements ClientRequestFilter {
private String user;
private String password;
private String integatorKey;
private String sendBehalfOf;
public DocuSignAuthenticator(String username, String password,
String integatorKey, String sendBehalfOf) {
this.username = username;
this.password = password;
this.integatorKey = integatorKey;
this.sendBehalfOf = sendBehalfOf;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(
"X-DocuSign-Authentication", getAuthenticationHeader());
}
private String getAuthenticationHeader() {
StringBuilder builder = new StringBuilder();
builder.append("<DocuSignCredentials>");
builder.append("<SendOnBehalfOf>");
builder.append(sendBehalfOf);
builder.append("</SendOnBehalfOf>");
builder.append("<Username>");
builder.append(username);
builder.append("</Username>");
builder.append("<Password>");
builder.append(password);
builder.append("</Password>");
builder.append("<IntegratorKey>");
builder.append(integatorKey);
builder.append("</IntegratorKey>");
builder.append("</DocuSignCredentials>");
return builder.toString();
}
}
And register it when creating a Client instance:
Client client = ClientBuilder.newClient().register(
new DocuSignAuthenticator(username, password, integatorKey, sendBehalfOf));

Is it possible to get TLS client auth cert info from jax-rs?

I am in an OSGi environment. I use jax-rs class for RESTServiceRegistration. I have configured the layers underneath to require client certification auth during the TLS handshake. Now I would like to access it from my service.
This is a pseudo code how I tried to access the X509 subject CommonName.
Interface (this is used for the REST webservice registration):
#GET
#WebMethod
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/tls")
public String soutTLSClientCertInfo(#Context SomeContext context);
This is the implementation:
#Override
public String soutTLSClientCertInfo(#Context SomeContext context) {
return context.getAttribute("java.etwas....tsl.cn_name");
}
I have found an answer...
HttpServletRequest is autoinjected if you have something like this
private HttpServletRequest request;
#Context public void setHttpServletRequest( HttpServletRequest request ) {
this.request = request;
}
Later you can use it like:
#Override
public String getTlsInfo() {
StringBuilder stringBuilder = new StringBuilder( );
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
for (X509Certificate cert: certs) {
stringBuilder.append(cert.getSubjectX500Principal().getName());
}
return stringBuilder;
}
You should also be careful with the javax.servlet version...

CXF RESTful Client - How to do basic http authentication without Spring?

I am familiar with using Jersey to create RESTful webservice servers and clients, but due to class loading issues, I am trying to convert a Jersey client into CXF. I believe I want to use an HTTP-centric client but we don't use Spring. We need to use basic HTTP authentication. The user guide has this example:
WebClient client = WebClient.create("http:books", "username", "password", "classpath:/config/https.xml");
The first parameter isn't a URI string. Is it a format used by Spring? Can this method only be used to create WebClients using Spring?
The other way of doing authentication shown is to add a header string:
String authorizationHeader = "Basic " + org.apache.cxf.common.util.Base64Utility.encode("user:password".getBytes());
webClient.header("Authorization", authorizationHeader);
I am guessing that "user:password" should be substituted with the real values, but would appreciate confirmation.
This answer came from the CXF users mailing list.
The first example referenced above had a typo in it. It has been updated to:
WebClient client = WebClient.create("http://books", "username", "password", "classpath:/config/https.xml");
The fourth argument can be null if a Spring config file is (and therefore Spring) is not being used.
So, this worked for me:
private WebClient webClient;
public RESTfulClient(String url, String username, String password)
throws IllegalArgumentException
{
this.username = username;
this.password = password;
this.serviceURL = url;
if (username == null || password == null || serviceURL == null)
{
String msg = "username, password and serviceURL MUST be defined.";
log.error(msg);
throw new IllegalArgumentException(msg);
}
webClient = WebClient.create(this.serviceURL,
this.username,
this.password,
null); // Spring config file - we don't use this
}

How do I consume a web service protected with HTTP basic authentication using the CXF framework?

I tried to get it to work using the CXF User Guide, but I've had no luck.
I'm trying to call the web service using java code.
This is covered by the JAX-WS Specification. Basically, set the username/password as properties on the request context:
((BindingProvider)proxy).getRequestContext().put(
BindingProvider.USERNAME_PROPERTY, "joe");
((BindingProvider)proxy).getRequestContext().put(
BindingProvider.PASSWORD_PROPERTY, "pswd");
The runtime puts them into the HTTP header.
You can provide your own Authenticator. That way it will work if the WDSL itself is protected by basic HTTP authentication.
#WebServiceRef(wsdlLocation = "https://laka/sito?wsdl")
static XxxService service;
public static void main(String[] args) {
Authenticator.setDefault(new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("user", "password".toCharArray());
}
});
service = new XxxService();
Xxx port = service.getXxxPort();
// invoke webservice and print response
XxxResponse resp = port.foo();
System.out.println(resp.toString());
}
There is a much better way:
when generating Java from WSDL, add option "-exsh true" :
wsdl2java -exsh true -p edu.sharif.ce http://wsdl.ir/WebServices/WebService.asmx?WSDL
and add UserCredential when using:
UserCredentials user = new UserCredentials();
user.setUserid("user");
user.setPassword("pass");
ResearchWebService_Service service = new ResearchWebService_Service();
ResearchWebService port = service.getResearchWebService();
port.addNewProject(newProject, user);

Categories