I'm trying to create a Web Service Client with Metro that uses WS-Security.
I have used Axis2, and to specify the username/password in an Axis2 client, I do:
org.apache.axis2.client.ServiceClient sc = stub._getServiceClient();
org.apache.axis2.client.Options options = sc.getOptions();
options.setUserName("USERNAME");
options.setPassword("PASSWORD");
How do I provide a username/password in a Metro client?
If you want to auth using basic http headers:
#WebEndpoint(name = "WSHttpBinding_ICustomerService")
public ICustomerService getWSHttpBindingICustomerService() {
WebServiceFeature wsAddressing = new AddressingFeature(true);
ICustomerService service =
super.getPort(new QName("http://xmlns.example.com/services/Customer",
"WSHttpBinding_ICustomerService"), ICustomerService.class,
wsAddressing);
Map<String, Object> context = ((BindingProvider)service).getRequestContext();
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Username", Collections.singletonList("yourusername"));
headers.put("Password", Collections.singletonList("yourpassword"));
return service;
}
If the service uses NTLM (Windows authentication) (explanation here):
#WebEndpoint(name = "WSHttpBinding_ICustomerService")
public ICustomerService getWSHttpBindingICustomerService() {
WebServiceFeature wsAddressing = new AddressingFeature(true);
ICustomerService service =
super.getPort(new QName("http://xmlns.example.com/services/Customer",
"WSHttpBinding_ICustomerService"), ICustomerService.class,
wsAddressing);
NtlmAuthenticator auth = new NtlmAuthenticator(username, password);
Authenticator.setDefault(auth);
return service;
}
Haven't used this myself, but seen other use it:
#WebEndpoint(name = "WSHttpBinding_ICustomerService")
public ICustomerService getWSHttpBindingICustomerService() {
WebServiceFeature wsAddressing = new AddressingFeature(true);
ICustomerService service =
super.getPort(new QName("http://xmlns.example.com/services/Customer",
"WSHttpBinding_ICustomerService"), ICustomerService.class,
wsAddressing);
Map<String, Object> context = ((BindingProvider)service).getRequestContext();
context.put(BindingProvider.USERNAME_PROPERTY, "yourusername");
context.put(BindingProvider.PASSWORD_PROPERTY, "yourpassword");
return service;
}
Related
Today I'm dealing with an issue creating a client to consume a restful service with a Bearer JWT. After appying the swagger codegen maven plugin I got the following GET operation what I need to consume in my new service:
public String getRelationByCucoIdUsingGET(Integer cucoId, String GS_AUTH_TOKEN) throws RestClientException {
Object postBody = null;
// verify the required parameter 'cucoId' is set
if (cucoId == null) {
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'cucoId' when calling getRelationByCucoIdUsingGET");
}
// create path and map variables
final Map<String, Object> uriVariables = new HashMap<String, Object>();
uriVariables.put("cucoId", cucoId);
String path = UriComponentsBuilder.fromPath("/getRelationByCucoId/{cucoId}").buildAndExpand(uriVariables).toUriString();
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
if (GS_AUTH_TOKEN != null)
headerParams.add("GS-AUTH-TOKEN", apiClient.parameterToString(GS_AUTH_TOKEN));
final String[] accepts = {
"application/json"
};
final List<MediaType> accept = apiClient.selectHeaderAccept(accepts);
final String[] contentTypes = { };
final MediaType contentType = apiClient.selectHeaderContentType(contentTypes);
String[] authNames = new String[] { };
ParameterizedTypeReference<String> returnType = new ParameterizedTypeReference<String>() {};
return apiClient.invokeAPI(path, HttpMethod.GET, queryParams, postBody, headerParams, formParams, accept, contentType, authNames, returnType);
}
The model class is as follow:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class CuCoPerson {
private Integer cucoId;
private List<CustomerRelation> customers;
}
And last, I have done this service but it doesnt work and I don't know how to create the controller to use this service.
#Service
public class CustomerRelationService {
RestControllerApi restControllerApi = new RestControllerApi();
public void getCustomers(Integer cudoId, Principal auth) {
restControllerApi.getRelationByCucoIdUsingGET(cudoId, auth.getName());
}
I am using JAXRS client to connect to my server. On the happy path, this works fine, but when my server throws an exception, the client receives Caused by: javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable
My Client Code is:
public interface ConfigClient {
#PUT
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/testMe")
Map<String, Object> saveAttributeStore(MyInput myInput);
}
public ConfigClient create(String url) {
final ResponseExceptionMapper exceptionMapper = response -> new RuntimeException();
final List<Object> providers = List.of(new JacksonJsonProvider(), exceptionMapper);
ConfigClient configClient = JAXRSClientFactory.create(url, ConfigClient.class, providers);
HTTPClientPolicy clientPolicy = WebClient.getConfig(configClient).getHttpConduit().getClient();
clientPolicy.setReceiveTimeout(1000);
clientPolicy.setConnectionTimeout(3000);
return configClient;
}
And Server Endpoint is:
#PutMapping(path = "testMe",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> createAttributeStore(MyInput input) {
return null;
}
i´m implementing a Restful service using Jax-RS 2.0 (Resteasy 3.0.7.Final) and share the interface between client and service.
The return value is void because ClientResponse is deprecated since RestEasy introduced JAX-RS 2.0 in version 3+.
To return the location of the new created object i inject the response, using the #Context annotation, and add the Content-Location header.
For example:
Shared Interface:
#Path("/")
#Consumes("application/xml")
#Produces("application/xml")
interface Resource {
#Path("createSomething")
void createSomething(AnyObject object);
...
}
Implementation class (The Service):
class ResourceImpl {
...
#Context org.jboss.resteasy.spi.HttpResponse response;
...
#Override
void createSomething(AnyObject object) throws AnyException {
String id = service.create(object);
response.getOutputHeaders().putSingle("Content-Location",
"/createSomething/" + id);
response.setStatus(Response.Status.CREATED.getStatusCode());
}
}
The client (build with the Resteasy Proxy Framework):
...
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target(baseUrl);
Resource resource = (Resource) target.proxy(Resource.class);
resource.createSomething(anyObject);
...
How can i retrieve Header information (and others, like Atom Links) which has been injected by the service?
Is it reasonable to use client side Filters and Interceptors?
Thank You
The best solution i found was to use a Filter to process the incoming response header.
public class HeaderFilter implements ClientResponseFilter {
private Map<String, String> headers = new HashMap<>();
private List<String> headerFilter = new ArrayList<>();
public final void addHeaderFilter(final String header) {
headerFilter.add(header);
}
public final void removeHeaderFilter(final String header) {
headerFilter.remove(header);
}
public final String getHeader(final String header) {
return headers.get(header);
}
#Override
public final void filter(final ClientRequestContext requestContext,
final ClientResponseContext responseContext)
throws IOException {
headers = new HashMap<>();
for (String headerToLookFor : headerFilter) {
String header = responseContext.getHeaderString(headerToLookFor);
if (header != null) {
headers.put(headerToLookFor, header);
} else {
...
}
}
}
}
I'm using socialauth to handle oauth authentication. Also I have basic login&password authentication(not implemented. but service method exists).
public Map<String, String> oauthAuthentication(String source, String callback) throws Exception {
Map<String, String> map = new HashMap<>();
SocialAuthConfig config = new SocialAuthConfig();
config.load("oauth_consumer.properties");
SocialAuthManager socialAuthManager = new SocialAuthManager();
socialAuthManager.setSocialAuthConfig(config);
String url = socialAuthManager.getAuthenticationUrl(source, callback);
map.put("url", url);
map.put("token", socialAuthManager.getCurrentAuthProvider().getAccessGrant().getKey());
return map;
}
public String loginPasswordAuthentication(String username, String password) {
return null;
}
But how to add spring security here? What should I keep and where? What authentication information I should keep? I have context for it and it works fine (intercept request and redirect to login page).
I am looking to build a stand-alone ExactTarget SOAP client using CXF.
I was able to create a client using Glassfish Metro, but due to future support considerations we would like to use CXF. I found an old example and associated project, but it is too old to be useful.
Currently I am trying to understand how can I set a handler on the stub/port object and to pass dynamic username and password to it. By dynamic I mean: the app gets username and password from the user at the time of running. Here is the code that I currently have for the Metro implementation:
PartnerAPI service = new PartnerAPI();
Soap stub = service.getSoap();
Map<String, Object> outProperties = new HashMap<String, Object>();
Map ctx = ((BindingProvider) stub).getRequestContext();
requestContext.put(BindingProvider.USERNAME_PROPERTY, user);
requestContext.put(BindingProvider.PASSWORD_PROPERTY, password);
List<Handler> chain = new ArrayList<Handler>();
chain.add(new SecurityHandler());
((BindingProvider) stub).getBinding().setHandlerChain(chain);
I am trying to reuse the first 4-6 lines for the CXF implementation, but I cannot use the handlers I have since they depend on com.sun.xml.wss.XWSSProcessor.
Here is code that does everything:
private static Soap createApiStub() {
PartnerAPI service = new PartnerAPI();
Soap stub = service.getSoap();
Client client = org.apache.cxf.frontend.ClientProxy.getClient(stub);
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, username);
outProps.put(WSHandlerConstants.PASSWORD_TYPE,WSConstants.PW_TEXT);
// Automatically adds a Base64 encoded message nonce and a created timestamp
outProps.put(WSHandlerConstants.ADD_UT_ELEMENTS,WSConstants.NONCE_LN + " " + WSConstants.CREATED_LN);
outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new ClientPasswordCallback(username, password));
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
client.getOutInterceptors().add(wssOut);
//Enable GZip compression
Map<String, java.util.List<String>> httpHeaders = new HashMap<String, java.util.List<String>>();
httpHeaders.put("Content-Encoding",Collections.singletonList("gzip"));
httpHeaders.put("Accept-Encoding",Collections.singletonList("gzip"));
Map<String, Object> reqContext = client.getRequestContext();
reqContext.put(MessageContext.HTTP_REQUEST_HEADERS,httpHeaders);
return stub;
}
And here is handler implementation:
public class ClientPasswordCallback implements CallbackHandler {
private String username;
private String password;
public ClientPasswordCallback(String username, String password) {
this.username = username;
this.password = password;
}
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (Callback callback: callbacks){
if (callback instanceof WSPasswordCallback){
WSPasswordCallback pc = (WSPasswordCallback) callback;
if (username.equals(pc.getIdentifier())) {
pc.setPassword(password);
}
} else if (callback instanceof NameCallback){
throw new UnsupportedCallbackException(callback);
} else {
throw new UnsupportedCallbackException(callback);
}
}
}
}
This answer helped me to pass the password dynamiclly.