getting http session cookies in spring-ws soap web service client - web-services

I have a SOAP web service client written with Spring-WS. In response to a logon request, the web service I connect to returns a session cookie in the HTTP response header with a token in it. Each subsequent call to the service requires that cookie with the token. How can I get the session cookie from the response and then add it to the HTTP header on subsequent calls to the service?
To summarize what I'm asking, how do you extract and inject cookies with Spring-WS.
Here is a snippet of code similar to what I am using to call into the web service:
MySessionLogon api = new MySessionLogon();
api.setUsername(username);
api.setPassword(password);
MySessionLogonResponse response = (MySessionLogonResponse) getWebServiceTemplate()
.marshalSendAndReceive(
"http://myserver/soapservice.asmx",
api,
new SoapActionCallback("http://www.myserver.com/MySession_Logon"));
return response;
I have searched the internet and read a lot of things that seem close to what I need, but I haven't seen a good way to get the value of the cookies I need to use. Any help would be appreciated, thanks in advance.

Did you check WebServiceMessageExtractor ?
You could do the following
MySessionLogonResponse response = (MySessionLogonResponse) getWebServiceTemplate()
.sendAndReceive(
"http://myserver/soapservice.asmx",
api,
new SoapActionCallback("http://www.myserver.com/MySession_Logon"),
new WebServiceMessageExtractor<ResponseAndHeader>() {
public ResponseAndHeader extractData(WebServiceMessage message) throws IOException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpComponentsConnection con = (HttpComponentsConnection) context.getConnection();
httpResponse httpResponse = con.getHttpResponse();
//use the method getHeaders from to retrieve the data you want
});

Related

IdentityServer4 + OWIN client: logout request not sending cookies to external IdP (because it is xhr)

The setup
I have an identity provider based on IdentityServer4 with AD FS as an external identity provider.
I have 3 different types of clients:
a production hybrid flow client based on OWIN lib
a test hybrid flow client (the latest sample client from the IdentityServer4 repo)
implicit flow clients (Angular)
The problem
The test client and the implicit flow clients work as expected.
When logging out from the OWIN client, the user is logged out from my IdP, but not from AD FS.
Some analysis
When logging out from the OWIN client, cookies are not sent when logging out from AD FS. In consequence, AD FS does not delete any cookies and thus the user stays logged in at the AD FS.
The ultimate reason is that the corresponding request is an xhr request which cannot send cookies (see also SAML logout request is not sending cookies to IdP, although we don't use SAML).
To summarize here are the calls you can observe in the browser:
Request GET https://myidp/connect/endsession
Query Parameters post_logout_redirect_uri=https://xxx, id_token_hint=xxx, x-client-SKU=ID_NET45, x-client-ver=5.5.0.0
Response Redirect to https://myidp/account/logout?logoutId=xxx
Request GET https://myidp/account/logout
Query Parameters logoutId=xxx
Response Redirect to https://myadfs/oauth2/logout?post_logout_redirect_uri=xxx&id_token_hint=xxx&state=xxx&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.5.0.0
Request GET https://myadfs/oauth2/logout
Request type xhr
Query parameters post_logout_redirect_uri=xxx, id_token_hint=xxx, state=xxx, x-client-SKU=ID_NETSTANDARD2_0, x-client-ver=5.5.0.0
Response Ok
For the OWIN client, the last call is of type xhr, for the other clients, it is of type document. I think something in the initial call from the client to myidp must be different such hat the redirect chain ends in a xhr request. What is this something? How can I change this behaviour?
I checked to headers of each call meticulously. Most suspect to me look the Sec-Fetch-Headers (the rest is pretty much equal for all clients), but I don't understand what they actually do.
For the OWIN client:
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
For the other clients:
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
I appreciate any hint!
The post I cited above actually was perfectly right: We have to interrupt the chain of xhr redirects and do a normal browser redirect instead.
However, we needed some time to figure out how to achieve this. Here is our solution:
The Logout action in our AuthorizationController returned Redirect, we had to replace it by a Ok response. This means replace
public class AuthorizationController : Controller
{
public ActionResult Logout()
{
Request.GetOwinContext().Authentication.SignOut("Cookies");
// ...Code for clearing cookies...
return Redirect("/myroute");
}
}
by
public class AuthorizationController : Controller
{
public ActionResult Logout()
{
Request.GetOwinContext().Authentication.SignOut("Cookies");
// ...Code for clearing cookies...
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
}

Get Token from destination response and pass to new Channel in Mirth

I've created a channel in Mirth which first calls authentication service and returns response. Source I've configured to HTTP Listener and in destination I've used HTTP Sender which calls a rest service for login and returns token as below.
{"token":"5912aa99-af21-5614-c232-d22be9e5c102","app":"TEST API Suite","expires":845,"userID":"ZCT06D123Cfk20oROB9x-bEXeY12oQGfack6dMMc-0o="}
Question: From the response that I'm receiving from rest service , how do I extract the token part and then pass that token to another channel and use it for calling another service.
Is there a way that I can set the token in a variable or global map in Destination1 and use that in Destination2 configuration?
Please Help. I'm a Newbie in Mirth.
Thanks.
The following helped me and solved my issue.
Add a post processor script as this in channel1:
var dest1 = responseMap.get("Destination 1");
var response123=dest1.getMessage();
var obj = JSON.parse(response123);
logger.info('token==='+obj.token);
router.routeMessage('newChannelName', obj.token);
return;
And then in newChannelName Channel preprocessor script, I was able to get this token as message.
logger.info('token: '+message);

HTTP error code: 302 when calling https webservice

I am trying to call a SOAP RPC style web service and getting the following error:
Exception in thread "main" com.sun.xml.internal.ws.client.ClientTransportException: The server sent HTTP status code 302:
This is a https web service and I have imported the certificate into cacerts thru browser but getting same result. Please note that, I can consume a REST webservice from the same machine without importing the certificate.
What I am missing when calling a SOAP service? Is it my client issue or something need to be done on the server side. I have access to the server.
HTTP status code 302 is a redirect, and so is unlikely due to a certificate problem. My initial guess is that you need to add a / (or remove it) from your URL. Some http server frameworks will redirect when a resource does not end in a /, so, instead of:
GET /myRpcEndpoint
Try
GET /myRpcEndpoint/
The other possibility is that this resource requires authentication and the server is redirecting you to a login page. If you want to know what is going on (and not guess), take a look a the the response headers for the 302. There will be a Location header telling you where the server wants you to go instead.
Had a similar issue where client code would receive a HTTP 302 error code when communicating with https and would work fine when communicating with http. In client code,you might need to specify the endpoint address on the request context using the BindingProvider.ENDPOINT_ADDRESS_PROPERTY property. Following the JAX-WS paradigm, the example below should work.
Please note that only the BindingProvider.ENDPOINT_ADDRESS_PROPERTY needs to be defined, the rest of your code should remain the same.
public static void main(String args[]) throws {
ObjectFactory factory = new ObjectFactory();
GetProducts_Service service = new GetProducts_Service();
GetProducts getProducts = service.getGetProductsPort();
final BindingProvider getProductsBP = (BindingProvider) getProducts;
getProductsBP.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"https://example.server.net/ExampleServicesWar/GetProducts");
GetProductsRequest request = factory.createGetProductsRequest();
GetProductsResponse response=getProducts.getProducts(request);
List<Product> products=response.getProducts();
}
All you have to is to use correct end point url
((BindingProvider)port).getRequestContext().put(BindingProvider.
ENDPOINT_ADDRESS_PROPERTY, "https://yourservice");
Need to import at the top:
import javax.xml.ws.BindingProvider;
port is Method call:
full source:
private static String getApplicationStatus(java.lang.String remoteAccessKey, java.lang.Integer responseId) {
net.quotit.oes._2010.ws.applicationstatusupdate.OASStatusUpdateService service = new net.quotit.oes._2010.ws.applicationstatusupdate.OASStatusUpdateService();
net.quotit.oes._2010.ws.applicationstatusupdate.IApplicationStatusUpdate port = service.getStatusUpdate();
((BindingProvider)port).getRequestContext().put(BindingProvider.
ENDPOINT_ADDRESS_PROPERTY, "https://servicename/basic");
return port.getApplicationStatus(remoteAccessKey, responseId);
}

Stub generated using Axis2 Webservice forming new connection for redirect URL...Need same TCP connection...!

I am badly stuck with a SOAP based integration using Axis2 framework for generation of client stubs from the Server WSDL. The scenario is as follows :
There is always a login API call first, which gives a Success response in SOAP body and Temporary Redirect in HTTP header. Also provides a URL which contains the session ID in the Location field of HTTP Header.
The next API call is required to be made at this redirect location. IN THE SAME TCP CONNECTION, for getting a proper response.
Now, the problem is, as a part of Webservice implementation using Axis2 generated stubs, I need to reload this redirect URL and re-instantiate it as --- "stub=new Stub(newurl)"
As soon as this is done, it creates a new TCP connection and so, the next request gives the response as "session ID invalid" because it goes out-of-sync with login API.
I have tried everything mentioned as a solution in this forum and nothing is working out.
For e.g --
MultiThreadedHttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager();
HttpClient httpClient = new HttpClient(httpConnectionManager);
ServiceClient serviceClient = stub._getServiceClient();
Options opts = stub._getServiceClient().getOptions();
opts.setTo(new EndpointReference(prop.getProperty("target_end_point_url")));
opts.setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Constants.VALUE_TRUE);
opts.setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
serviceClient.setOptions(opts);
stub._setServiceClient(serviceClient);
Similarly, I have tried many other options too. But it's not helpful at all.
Faced exactly the same issue.
Following steps solved the issue.
1. Using HttpClient, perform login. Don't use stub object to perform login.
2. Use the Location Header URL, to create new stub object i.e. stub = new Stub(locationURL). (Your existing options setting should be retained.)
3. There is a default timeout, by which server disconnects the TCP connection. In my case it was 50 seconds. Hence as soon as i performed login in step 1, i execute a timer every 40 seconds, to send an empty requests to new Location URL using HeadMethod of same HttpClient object.

Setting HTTP headers through Axis2 API

I am using apache axis2 server webservies, Basically I am sending xml response to android client through webservices. Here I need to maintain the session since the services per user basis. I know maintaining session in webservices is bad idea, but cant avoid it.
Actually I need to generate random unique string when user invoke first service from android client, that random string going to be used as session id. This session id, i need to set in http custom header, so that android client can able to get it and can send it subsequent requests as well.
I want to know whether any API is available in axis2 to set custom header information on http headers. Same way I need to read the http header, so that next request I can get the session id from header.
Can anyone advice me regarding this?? Thanks
-Ravi
Dead link on #Martin Dürrmeier's answer, here's a snapshot of the webpage that i've found on web.archive.org : Axis2 - Setting custom HTTP Headers on a response, it helped me.
Here's the lines needed :
MessageContext responseMessageContext =
MessageContext.getCurrentMessageContext().getOperationContext().getMessageContext(
WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
List<Header> headers = new ArrayList<Header>();
headers.add(new Header(HTTPConstants.HEADER_CONTENT_ENCODING, "identity"));
responseMessageContext.setProperty(HTTPConstants.HTTP_HEADERS, headers);