Java Stateful Sessions or How to Properly Send Cookies With Each Redirect Request
Sometimes you need to log in to some webpage programmatically. I ran into one of
those pages where, when the login succeeds, you’re being redirected to another
page, and then to another (something along the lines of ‘Please login’ -> ‘You
are successfully logged in’ -> ‘Admin panel’). So I needed to write something
that would store the cookies that come along with each response, and send those
cookies out with each subsequent request. With curl
, I would have used:
$ curl --location --cookie-jar logincookie 'https://login.securepage.com'
So how can we emulate this behavior in Java?
Java SE’s CookieHandler
A simple DuckDuckGo search
will show Java SE’s own
java.net.CookieHandler
:
pretty neat, a built-in solution! The CookieHandler
, as the name
suggests, will handle all your floating-point arithmetic. Just kidding,
it will serve as the object where the HTTP protocol handler (used by
objects such as URL
) will go to to check for relevant cookies.
CookieHandler
is a global abstract class that will handle the state
management of all requests.
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cookieManager);
CookieManager
’s
task is to decide which cookies to accept and which to reject.
CookiePolicy.ACCEPT_ALL
will accept all cookies. Other choices include
CorkiePolicy.ACCEPT_NONE
(no cookies will be accepted) and
CookiePolicy.ACCEPT_ORIGINAL_SERVER
(only cookies from the original server will be accepted).
If you want, you can also create a CookieManager
with a specific
CookieStore
and specify exactly where your cookies will be stored. This is needed,
for example, to create persistent cookies that can be restored after a
JVM restart. By default, CookieManager
will create an in-memory cookie
store.
To install our own system-wide cookie handler, we invoke
CookieHandler.setDefault
and pass our CookieManager
as an argument.
The tutorial on the Oracle website does a pretty decent job of explaining everything in more detail.
If this works for you, great! If not, keep on reading.
Apache HttpComponent Client
to the rescue
Unfortunately, the steps above did not work for me as the server kept
complaining that the session was invalid after the redirects. After
reading somewhere on StackOverflow that Java SE’s version of the
CookieStore
was buggy, and me being on a deadline, I decided to turn
my attention to Apache and their HttpComponent Client
(version 4.5.3):
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
// ... other imports
class Downloader {
private CookieStore cookieStore = new BasicCookieStore();
private void doLogin() {
// CookieSpecs.STANDARD is the RFC 6265 compliant policy
RequestConfig requestConfig = RequestConfig
.custom()
.setCookieSpec(CookieSpecs.STANDARD)
.build();
// automatically follow redirects
CloseableHttpClient client = HttpClients
.custom()
.setRedirectStrategy(new LaxRedirectStrategy())
.setDefaultRequestConfig(requestConfig)
.setDefaultCookieStore(cookieStore)
.build();
String addr = "https://login.securehost.com";
HttpPost post = new HttpPost();
// login and password as payload for POST request
List<NameValuePair> urlParams = new ArrayList<>();
urlParams.add(new BasicNameValuePair("login", "neftas"));
urlParams.add(new BasicNameValuePair("password", "letmein"));
post.setEntity(new UrlEncodedFormEntity(urlParams));
HttpResponse response = client.execute(post);
// ... and we are logged in!
}
}
(To keep the code clean, I haven’t bothered with any exception handling.)
The RequestConfig
serves to indicate what kind of cookie policy we want
(there are
several).
If you just want things to work, you can start with CookieSpecs.STANDARD
,
which is compliant with “a more relaxed profile defined by RFC 6265”.
During the creation of the HttpClient
, we specify that we want to
follow all redirects (setRedirectStrategy(new LaxRedirectStrategy())
),
we pass the cookie policy we defined earlier in the RequestConfig
, and
lastly, we pass the cookie store.