JAX-RS 2 Client: maintain the session between requests
JAX-RS 2 has a good client API but it doesn't provide a solution to maintain the session out of the box. How to do it?
Until CXF 3.1.9 the old CXF WebClient solution was not working without some work (it has been fixed for 3.1.10 and you can just set http.maintain.session=true in client properties).
With Jersey, you need to switch the actual client implementation to httpclient or another transport supporting session handling then configure it.
Nothing really portable or nice to use. So how to solve that issue?
How is a session maintained in a browser?
When you browse a website using the session (whatever it is, java, PHP, other) the browser does a lot of tricks behind the scene to hide the required work between requests to ensure you reuse the same session. It is a bit like when you authenticate with this ugly popup for Basic authentication and the browser propagate the Authorization header automatically.
So what's involved to maintain the session? In Servlet containers - and most servers actually - you have 3 modes:
- Cookie: a cookie named JSESSIONID (by default) holds the session id and ensures the server can find it back, here the browser sends automatically this cookie for each requests once created
- URL: same idea as for cookies but using a path parameters, by default it is the jsessionid parameter in the URL.
- Note this one is good cause doesn't require the browser to support Cookies (but today which browser doesn't?) but has the drawback to put the id in the URl which can be a security issue and also a SEO issue.
- SSL: the request has an attribute with the session id, it is named javax.servlet.request.ssl_session_id
Link with JAX-RS Client
What does it mean for our client? That we just have to find how the session is maintained and propagate the right information to the next request.
Between the 3 modes we talked about before, we don't care much about SSL one since this is a server trick but client handling is generally Cookie or URL.
Cookie case
For the cookie case - most of website will rely on this one - you just need to use a request returning a Response to be able to read the Cookie and set it on next request:
final Client client = ClientBuilder.newClient();
try {
final WebTarget target = client.target("https://superhost.com");
// 1
final Response first = target
.path("create-session")
.get();
// 2
final Payload1 data = first.readEntity(Payload1.class);
// 3
final Cookie sessionId = first.getCookies().get("JSESSIONID");
final Payload2 second = target
.path("need/to/be/logged")
// 4
.cookie(sessionId)
.get(Payload2.class);
} finally {
client.close();
}
- We do our first request creating the session (often you will login there too) and get the Response and not directly our response payload (Payload1)
- We extract our payload since it is often what is important when doing a request and can process it if needed
- We extract the session id from the cookies
- We propagate the session cooke to the next request
Note: of course you can propagate all cookies (like a browser) between requests but it is not mandatory. Also note it is easy to wrap the JAX-RS client API to handle this propagation automatically.
URL case
This case is not as easy as previous one cause if you don't have any redirection once the session created you will not see the jsessionId parameter and will not be able to propagate it. However if you are redirected you can get the Location from the Response and extract jsessionId parameter.
Note however that this is rare to have this case activated without the Cookie one so it is not a very big deal.
Conclusion
Maintaining the session with a JAX-RS client is finally as difficult as with any client but the good news is that it is not more difficult :).
Since most servers will rely on cookies it is finally very doable to implement it even if the manual propagation is quite painful for API relying on the session heavily. In such a case I would recommand you to wrap the client API to avoid some headache if you forget it on one request or if you try to reuse a single id. The server can, in some cases, change the session id for security reasons (this means you need to read it from each response to be safe and link the propagation between requests).
From the same author:
In the same category: