45

I have a server request that returns multiple cookies, like that:

enter image description here

This is how I'm storing these cookies to the cookieManager:

HttpURLConnection connection = ... ;
static java.net.CookieManager msCookieManager = new java.net.CookieManager();
msCookieManager.put(COOKIES_URI, connection.getHeaderFields());

This is how I'm adding these cookies to the next connection:

connection.setRequestProperty("Cookie", 
  msCookieManager.getCookieStore().get(COOKIES_URI).toString());

Is it the right way to get the cookies from the cookieManager?, I'm quite sure there is a better one...

Community
  • 1
  • 1
David
  • 37,109
  • 32
  • 120
  • 141

5 Answers5

108

Ok, the right way to do it is just like that:

Get Cookies from response header and load them into cookieManager:

static final String COOKIES_HEADER = "Set-Cookie";
HttpURLConnection connection = ... ;
static java.net.CookieManager msCookieManager = new java.net.CookieManager();

Map<String, List<String>> headerFields = connection.getHeaderFields();
List<String> cookiesHeader = headerFields.get(COOKIES_HEADER);

if (cookiesHeader != null) {
    for (String cookie : cookiesHeader) {
        msCookieManager.getCookieStore().add(null,HttpCookie.parse(cookie).get(0));
    }               
}

Get Cookies from cookieManager and load them into connection:

if (msCookieManager.getCookieStore().getCookies().size() > 0) {
    // While joining the Cookies, use ',' or ';' as needed. Most of the servers are using ';'
    connection.setRequestProperty("Cookie",
    TextUtils.join(";",  msCookieManager.getCookieStore().getCookies()));    
}
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
David
  • 37,109
  • 32
  • 120
  • 141
  • 1
    When should you read the cookies? Right after opening the connection? Thanks. – Abushawish Oct 28 '14 at 20:22
  • 2
    +1 When using multiple cookies I had more luck with using a semicolon: `TextUtils.join(";", msCookieManager.getCookieStore().getCookies()));` – Reafidy Apr 25 '15 at 10:35
  • 2
    Woah buddy! I've been trying to do this with an http keepalive / httpurlconnection persistent connection when THIS is the correct way to handle my login session. Thank you for this answer! – Jacksonkr Feb 02 '16 at 14:11
  • 1
    Thank you so much, this was a life saver after I spent hours struggling with CookieHandler.setDefault – Constantinos Jun 01 '16 at 11:33
  • 1
    Hi, I tried this one, but when I add the requestProperty for the Cookie got this error java.lang.IllegalStateException: Cannot set request property after connection is made – Aaron Jan 25 '17 at 09:46
  • 2
    This code can't be right because it sends all the cookies you have for all sites. Not only those belonging to server set them, but all. This is security issue. – Alexander Dyagilev Apr 10 '20 at 17:15
  • `HttpCookie.parse(cookie)` adds quotes. Eg. I get this header from response `Set-Cookie: session=12345`. When load cookies in url connection, it is sending like `session="12345"` in Cookie header. How can I send cookies without quotes ? – Khushbu Shah Jan 07 '21 at 04:16
  • Found the solution for quotes. Setting version 0 of HttpCookie before adding it into Cookie store will not add quotes. Version 1 will add quotes as per RFC. – Khushbu Shah Jan 07 '21 at 05:37
  • Cannot believe this is the accepted answer and has so many upvotes. This sends ALL cookies ever received to ALL requests ever made. @AlexanderDyagilev is right in pointing out that this is a security issue first of all. Secondly, it ignores all the options the cookie may have, like the Expires field. See here for a full list of options a cookie may have: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie – TaylanKammer Jan 04 '23 at 05:44
58

I've been searching/trying for days to fix my issue: cannot access protected web resources even after logging in successfully

I created the same app on iOS and didn't have the same problem because NSUrlConnection did the cookie maintenance for us behind the scene. On Android, I tried manually adding cookie

connection.setRequestProperty("Cookie", "PHPSESSID=str_from_server")

without any luck.

Finally I read this

and added the following 2 lines somewhere in the beginning of my app:

CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);

and everything works fine now.

Golden Thumb
  • 2,531
  • 21
  • 20
  • 2
    Works great thanks, only those 2 lines are needed yay ^_^, tried the other answer too both works but this one is more convenient – Shereef Marzouk Feb 27 '14 at 15:18
  • 2
    This should be the accepted answer. I've tried to handle cookies manually without success. These two lines are just what you need to handle cookies with HttpUrlConnection! Thanks Gold Thumb ! – Bertrand Feb 03 '16 at 06:48
  • This didn't quite work for me, cookies were sent with first couple of requests but not with subsequent ones, I have no idea why... – szx Apr 17 '16 at 05:29
  • 2
    @HeisenBerg put anywhere as long as it is executed once, and before your first use of `HttpURLConnection`. – vegemite4me May 09 '16 at 16:59
  • This doesn't compile on my AS 2.3.3 As typical of this environment.... Always a moving target, and never backward compatible. `CookieHandler.setDefault(...)` doesn't take a data type of `cookieManager`. – SpacemanScott Sep 20 '18 at 14:38
  • **Update** Strike that above.... It is necessary to use the correct "import". And there is more than one that uses the same identifier. (why that was done is another mystery) – SpacemanScott Sep 20 '18 at 14:57
  • added those two lines above and loaded my page `cookieManager.getCookieStore().getCookies().size` returned `2`. Works good! – annoying_squid Dec 14 '18 at 15:20
3

@David's answer is the best of the lot. Its easiest to maintain a local CookieManager and manually write into and read from the cookie store associated with this cookie manager.

This code loads the Cookies from a response into the cookie manager :

/**
 * Gets Cookies from the response header and loads them into cookie manager
 *
 * @param conn          instance of {@link HttpURLConnection} object
 * @param cookieManager the cookie manager({@link CookieManager} instance) in which the cookies are to be loaded<p>In case a null object is passed, the function will not perform any action and return back to the caller. </p>
 */
public static void loadResponseCookies(@Nullable HttpURLConnection conn,@Nullable CookieManager cookieManager) {

    //do nothing in case a null cokkie manager object is passed
    if (cookieManager == null || conn == null){
        return;
    }

    List<String> cookiesHeader = conn.getHeaderFields().get(COOKIES_HEADER);
    if (cookiesHeader != null) {
        for (String cookieHeader : cookiesHeader) {
            List<HttpCookie> cookies;
            try {
                cookies = HttpCookie.parse(cookieHeader);
            } catch (NullPointerException e) {
                log.warn(MessageFormat.format("{0} -- Null header for the cookie : {1}",conn.getURL().toString(), cookieHeader.toString()));
                //ignore the Null cookie header and proceed to the next cookie header
                continue;
            }

            if (cookies != null) {
                Debug("{0} -- Reading Cookies from the response :", conn.getURL().toString());
                for (HttpCookie cookie : cookies) {
                    Debug(cookie.toString());
                }
                if (cookies.size() > 0) {
                    cookieManager.getCookieStore().add(null, HttpCookie.parse(cookieHeader).get(0));
                }
            }
        }
    }
}

This code populates the HttpUrlConnection object with the cookies associated with the cookie manager :

public void populateCookieHeaders(HttpURLConnection conn) {

    if (this.cookieManager != null) {
        //getting cookies(if any) and manually adding them to the request header
        List<HttpCookie> cookies = this.cookieManager.getCookieStore().getCookies();

        if (cookies != null) {
            if (cookies.size() > 0) {
                Debug("{0} -- Adding Cookie Headers : ", url.toString());
                for (HttpCookie cookie : cookies) {
                    Debug(cookie.toString(), null);
                }

                //adding the cookie header
                conn.setRequestProperty(COOKIE_REQUEST_HEADER, StringUtils.join(cookies, ";"));
            }
        }
    }
}

This is the most thread safe way to handle cookies.

I tried using a threadlocal cookiestore and an extension of CookieManager. Neither of these approaches worked in my case.

Aditya Satyavada
  • 1,028
  • 1
  • 10
  • 14
  • There is no `conn.getHeaderFields()` member to the `HttpURLConnection` object. – SpacemanScott Sep 20 '18 at 15:02
  • @SpacemanScott That would depend on which version of Java you were using. I'd given this solution when I was using Java 7. This is the link:https://docs.oracle.com/javase/7/docs/api/java/net/URLConnection.html#getHeaderFields() to the documentation. It's possible that this function was deprecated in whatever version of Java you're using now ? – Aditya Satyavada Jan 07 '19 at 05:11
  • Who the heck can tell what version I'm using?? This is Android Studio and it is so bloated and obfuscated, I have no idea what it's doing "under the covers". The setting just says "use javac". But I know it's not either of the 32 or 64 bit compilers that I installed. It's got it's own, buried somewhere, which you apparently are not allowed to know anything about. – SpacemanScott Jan 08 '19 at 14:12
  • @SpacemanScott You can go to the terminal in Android Studio and simply type `java --version`. It will tell you the version of java being used. – Aditya Satyavada Jan 11 '19 at 04:46
3

The other answers are appropriate for their contexts, but nobody is saying the obvious: CookieHandler automatically deals with your cookies in most cases. To just maintain cookies from one request to the next -- for instance to maintain a server session -- then this line of code is sufficient:

CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));

Make sure to run it once before your first request with UrlConnection.

Now I do zero cookie management myself, and all of my server session data is flawlessly maintained from request to request.

Am I crazy? Why is the internet silent on this? I can't even find it in the docs.

Malachi Holden
  • 275
  • 1
  • 12
  • 1
    You're right, this works. Amazing. The accepted top comment not only contains tons of unnecessary code when this one-liner is enough, it's also a serious security issue because it sends all cookies ever received to all hosts ever connected to. I think this is the worst case of a bad top answer I've ever seen on StackOverflow. – TaylanKammer Jan 04 '23 at 05:48
0

using java.net.HttpURLConnection and groovy, simply add URLConnection#addRequestProperty(String key, String value) inside with closure.

import java.net.HttpURLConnection

HttpURLConnection createRequest(String chatRequestBody) {
    HttpURLConnection httpChatRequest = new URL("${endpoint}").openConnection() as HttpURLConnection

    httpChatRequest.with {
        doOutput = true
        requestMethod = 'POST'

        //headers
        addRequestProperty("Content-Type", "application/json")
        addRequestProperty("Client-Platform", "android")

        //cookies
        addRequestProperty("Cookie", "cookie_key1=cookie_value1,cookie_key2=cookie_value2")

        getOutputStream().write(chatRequestBody.getBytes("UTF-8"))
    }
    httpChatRequest
}
prayagupa
  • 30,204
  • 14
  • 155
  • 192