如何使用cookieManager在httpUrlConnection中处理cookies

45

我有一个请求服务器,返回多个cookie,如下所示:

enter image description here

这是我将这些cookie存储到cookie管理器中的方法:

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

这就是我如何将这些cookie添加到下一个连接中:

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

从cookieManager获取cookie的方式是否正确?我相信有更好的方法...

5个回答

108

好的,正确的做法就是这样:

从响应头中获取Cookies并将其加载到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));
    }               
}
从cookieManager获取Cookies并将它们加载到连接中:
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()));    
}

1
你应该在什么时候读取cookies?是在打开连接后立即读取吗?谢谢。 - Abushawish
2
当使用多个cookie时,我更喜欢使用分号:TextUtils.join(";", msCookieManager.getCookieStore().getCookies())); - Reafidy
2
哇伙计!我一直在尝试使用http keepalive / httpurlconnection持久连接来做这件事情,但是原来正确的方式是处理我的登录会话。谢谢您的答案! - Jacksonkr
1
非常感谢,这真是救了我一命,因为我花了几个小时来解决CookieHandler.setDefault的问题。 - Constantinos
2
这段代码不正确,因为它会发送你所有站点的所有 cookie,而不仅仅是服务器设置的那些。这是一个安全问题。 - Alexander Dyagilev
显示剩余4条评论

58

我已经搜索/尝试了几天来解决我的问题:即使成功登录,也无法访问受保护的Web资源。

我在iOS上创建了相同的应用程序,并没有遇到同样的问题,因为NSUrlConnection在幕后为我们处理了cookie维护。在Android上,我尝试手动添加cookie。

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

没有任何运气。

最终,我阅读了这个

并在我的应用程序开头添加了以下2行:

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

现在一切都正常运作了。


2
非常好,谢谢。只需要这两行代码就可以了,太棒了 ^_^,我也试过另一个答案,都可以用,但这个更方便。 - Shereef Marzouk
2
这应该是被接受的答案。我曾试图手动处理cookie,但没有成功。这两行代码就是使用HttpUrlConnection处理cookie所需的全部!感谢Gold Thumb! - Bertrand
这对我来说并不完全有效,cookie随着前几个请求被发送,但在后续请求中没有发送,我不知道为什么... - szx
2
@HeisenBerg 放在任何地方都可以,只要它被执行一次,且在您第一次使用 HttpURLConnection 之前。 - vegemite4me
我在上面添加了两行代码并加载了我的页面,cookieManager.getCookieStore().getCookies().size 返回了 2。运行良好! - annoying_squid
显示剩余2条评论

3

@David的回答是最好的。最容易维护一个本地CookieManager,手动将数据写入和读取与此cookie管理器相关联的cookie存储中。

以下代码将响应中的Cookies加载到cookie管理器中:

/**
 * 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));
                }
            }
        }
    }
}

这段代码将 HttpUrlConnection 对象与 cookie 管理器相关联的 cookies 填充:

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, ";"));
            }
        }
    }
}

这是处理cookie最安全的方式。

我尝试使用了threadlocal cookiestore和CookieManager的扩展。但是这两种方法都不适用于我的情况。


HttpURLConnection 对象中没有 conn.getHeaderFields() 成员。 - SpacemanScott
@SpacemanScott 这取决于您使用的Java版本。当我使用Java 7时,我提供了这个解决方案。这是链接:https://docs.oracle.com/javase/7/docs/api/java/net/URLConnection.html#getHeaderFields() 到文档。可能在您现在使用的Java版本中,此函数已被弃用? - Aditya Satyavada
谁能告诉我我在用哪个版本?这是Android Studio,它非常臃肿和混淆,我不知道它在“幕后”做什么。设置只说“使用javac”。但我知道它既不是我安装的32位编译器也不是64位编译器。它有自己的编译器,藏在某个地方,显然你不能知道任何相关信息。 - SpacemanScott
@SpacemanScott 你可以进入Android Studio终端,直接输入 java --version。它会告诉你正在使用的Java版本。 - Aditya Satyavada

3
其他答案都适用于它们所在的上下文,但没有人说明显的事情:大多数情况下,CookieHandler会自动处理您的 cookie。仅仅从一个请求到下一个请求来保持 cookie -- 比如保持一个服务器 session -- 那么这行代码就足够了:
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));

在使用UrlConnection发送第一个请求前,请确保至少运行一次。

现在我不再自己处理cookie管理,而我的所有服务器会话数据都可以在请求之间无缝维护。

我疯了吗?为什么互联网上对此保持沉默?我甚至在文档中找不到相关内容。


1
你说得对,这个方法可行。太神奇了。被采纳的最高评论不仅包含大量不必要的代码,而且还存在严重的安全问题,因为它将曾经接收到的所有 cookies 发送给曾经连接过的所有 hosts。我认为这是我在 StackOverflow 上见过的最糟糕的顶级答案之一。 - TaylanKammer

0

使用 java.net.HttpURLConnection 和 groovy,只需在 with 闭包内添加 URLConnection#addRequestProperty(String key, String value) 即可。

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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接