CookieManager用于多个线程

我试图通过线程建立多个连接。

但是每个连接似乎都会覆盖其他连接,导致连接使用错误的cookie。

在线程类的构造函数内:

manager = new CookieManager(); manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); CookieHandler.setDefault(manager); 

有没有办法管理每个线程或每个类的cookie?

新的失败尝试:

现在每个线程都使用它自己的索引,但它们似乎仍然以cookie方式覆盖彼此。 有任何想法吗?

 public class threadedCookieStore implements CookieStore, Runnable { CookieStore[] store = new CookieStore[1000]; int index; public threadedCookieStore(int new_index) { index = new_index; // get the default in memory cookie store store[index] = new CookieManager().getCookieStore(); // todo: read in cookies from persistant storage // and add them store // add a shutdown hook to write out the in memory cookies Runtime.getRuntime().addShutdownHook(new Thread(this)); } public void run() { // todo: write cookies in store to persistent storage } public void add(URI uri, HttpCookie cookie) { store[index].add(uri, cookie); } public List get(URI uri) { return store[index].get(uri); } public List getCookies() { return store[index].getCookies(); } public List getURIs() { return store[index].getURIs(); } public boolean remove(URI uri, HttpCookie cookie) { return store[index].remove(uri, cookie); } public boolean removeAll() { return store[index].removeAll(); } } 

在课堂上:

 threadedCookieStore cookiestore = new threadedCookieStore(index); manager = new CookieManager(cookiestore,CookiePolicy.ACCEPT_ALL); manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); CookieHandler.setDefault(manager); 

感谢大家。

我赞成所有的答案,但没有一个有完整的解决方案。

由于google’ing这个问题导致这个页面在这里,我将发布完整的解决方案并接受我自己的答案:

如何:

1将CookieHandler扩展到SessionCookieManager

这是基于如何使用HttpURLConnection和Java中的CookieManager为每个连接使用不同的cookie ,nivs正确描述它,不提供完整的解决方案。 所以大多数/所有的功劳归于他,我只是制作完整的HowTo。 SessionCookieManager基于Java的源代码http://docs.oracle.com/javase/7/docs/api/java/net/CookieManager.html

 import java.io.IOException; import java.net.CookieHandler; import java.net.CookiePolicy; import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; public class SessionCookieManager extends CookieHandler { private CookiePolicy policyCallback; public SessionCookieManager() { this(null, null); } private final static SessionCookieManager ms_instance = new SessionCookieManager(); public static SessionCookieManager getInstance() { return ms_instance; } private final static ThreadLocal ms_cookieJars = new ThreadLocal() { @Override protected synchronized CookieStore initialValue() { return new InMemoryCookieStore(); } }; public void clear() { getCookieStore().removeAll(); } public SessionCookieManager(CookieStore store, CookiePolicy cookiePolicy) { // use default cookie policy if not specify one policyCallback = (cookiePolicy == null) ? CookiePolicy.ACCEPT_ALL //note that I changed it to ACCEPT_ALL : cookiePolicy; // if not specify CookieStore to use, use default one } public void setCookiePolicy(CookiePolicy cookiePolicy) { if (cookiePolicy != null) policyCallback = cookiePolicy; } public CookieStore getCookieStore() { return ms_cookieJars.get(); } public Map> get(URI uri, Map> requestHeaders) throws IOException { // pre-condition check if (uri == null || requestHeaders == null) { throw new IllegalArgumentException("Argument is null"); } Map> cookieMap = new java.util.HashMap>(); // if there's no default CookieStore, no way for us to get any cookie if (getCookieStore() == null) return Collections.unmodifiableMap(cookieMap); List cookies = new java.util.ArrayList(); for (HttpCookie cookie : getCookieStore().get(uri)) { // apply path-matches rule (RFC 2965 sec. 3.3.4) if (pathMatches(uri.getPath(), cookie.getPath())) { cookies.add(cookie); } } // apply sort rule (RFC 2965 sec. 3.3.4) List cookieHeader = sortByPath(cookies); cookieMap.put("Cookie", cookieHeader); return Collections.unmodifiableMap(cookieMap); } public void put(URI uri, Map> responseHeaders) throws IOException { // pre-condition check if (uri == null || responseHeaders == null) { throw new IllegalArgumentException("Argument is null"); } // if there's no default CookieStore, no need to remember any cookie if (getCookieStore() == null) return; for (String headerKey : responseHeaders.keySet()) { // RFC 2965 3.2.2, key must be 'Set-Cookie2' // we also accept 'Set-Cookie' here for backward compatibility if (headerKey == null || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie") ) ) { continue; } for (String headerValue : responseHeaders.get(headerKey)) { try { List cookies = HttpCookie.parse(headerValue); for (HttpCookie cookie : cookies) { if (shouldAcceptInternal(uri, cookie)) { getCookieStore().add(uri, cookie); } } } catch (IllegalArgumentException e) { // invalid set-cookie header string // no-op } } } } /* ---------------- Private operations -------------- */ // to determine whether or not accept this cookie private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) { try { return policyCallback.shouldAccept(uri, cookie); } catch (Exception ignored) { // pretect against malicious callback return false; } } /* * path-matches algorithm, as defined by RFC 2965 */ private boolean pathMatches(String path, String pathToMatchWith) { if (path == pathToMatchWith) return true; if (path == null || pathToMatchWith == null) return false; if (path.startsWith(pathToMatchWith)) return true; return false; } /* * sort cookies with respect to their path: those with more specific Path attributes * precede those with less specific, as defined in RFC 2965 sec. 3.3.4 */ private List sortByPath(List cookies) { Collections.sort(cookies, new CookiePathComparator()); List cookieHeader = new java.util.ArrayList(); for (HttpCookie cookie : cookies) { // Netscape cookie spec and RFC 2965 have different format of Cookie // header; RFC 2965 requires a leading $Version="1" string while Netscape // does not. // The workaround here is to add a $Version="1" string in advance if (cookies.indexOf(cookie) == 0 && cookie.getVersion() > 0) { cookieHeader.add("$Version=\"1\""); } cookieHeader.add(cookie.toString()); } return cookieHeader; } static class CookiePathComparator implements Comparator { public int compare(HttpCookie c1, HttpCookie c2) { if (c1 == c2) return 0; if (c1 == null) return -1; if (c2 == null) return 1; // path rule only applies to the cookies with same name if (!c1.getName().equals(c2.getName())) return 0; // those with more specific Path attributes precede those with less specific if (c1.getPath().startsWith(c2.getPath())) return -1; else if (c2.getPath().startsWith(c1.getPath())) return 1; else return 0; } } } 

请注意,在我的情况下,我将CookiePolicy的默认值CookiePolicyACCEPT_ALL

2在全局范围内,在运行任何线程之前,请调用:

 CookieHandler.setDefault(SessionCookieManager.getInstance()); 

3线程完成后,在其中调用:

 SessionCookieManager.getInstance().clear(); 

再次:不是我的想法,只是把它放在一起。 所有功劳都归功于Java和https://stackoverflow.com/users/1442259/nivs

谢谢,我试图使用你的答案,但它是基于旧版本的CookieManager(可能为什么你必须使用ACCEPT_ALL)并引用了包私有的InMemoryCookieStore,所以它激发了我最终的解决方案。 以前应该对我们所有人都很明显:一个ThreadLocal CookieStore代理类。

 CookieHandler.setDefault(new CookieManager(new ThreadLocalCookieStore(), null)); 

 import java.net.CookieManager; import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; import java.util.List; public class ThreadLocalCookieStore implements CookieStore { private final static ThreadLocal ms_cookieJars = new ThreadLocal() { @Override protected synchronized CookieStore initialValue() { return (new CookieManager()).getCookieStore(); /*InMemoryCookieStore*/ } }; @Override public void add(URI uri, HttpCookie cookie) { ms_cookieJars.get().add(uri, cookie); } @Override public List get(URI uri) { return ms_cookieJars.get().get(uri); } @Override public List getCookies() { return ms_cookieJars.get().getCookies(); } @Override public List getURIs() { return ms_cookieJars.get().getURIs(); } @Override public boolean remove(URI uri, HttpCookie cookie) { return ms_cookieJars.get().remove(uri, cookie); } @Override public boolean removeAll() { return ms_cookieJars.get().removeAll(); } } 

似乎对我来说就像一个魅力

您可以安装一个管理ThreadLocal CookieManager实例的CookieHandler。

DavidBlackledge的ThreadLocal CookieStore是最好的方式。 为了提高内存效率,我在这里提供了一个常规CookieStore的简单实现,因此您不必为每个线程实例化一个完整的CookieManager (假设您不仅仅有几个)。

 /** * @author lidor * A simple implementation of CookieStore */ public class CookieJar implements CookieStore { private Map> jar; private List freeCookies; public CookieJar() { jar = new HashMap>(); freeCookies = new ArrayList(); } @Override public void add(URI uri, HttpCookie cookie) { if (uri != null) { if (!jar.containsKey(uri)) jar.put(uri, new ArrayList()); List cookies = jar.get(uri); cookies.add(cookie); } else { freeCookies.add(cookie); } } @Override public List get(URI uri) { Log.trace("CookieJar.get (" + this + ") called with URI " + uri + " (host=" + uri.getHost() + ")"); List liveCookies = new ArrayList(); if (jar.containsKey(uri)) { for (HttpCookie cookie : jar.get(uri)) { if (!cookie.hasExpired()) liveCookies.add(cookie); } } for (HttpCookie cookie : getCookies()) { if (cookie.getDomain().equals(uri.getHost())) if (!liveCookies.contains(cookie)) liveCookies.add(cookie); } return Collections.unmodifiableList(liveCookies); } @Override public List getCookies() { List liveCookies = new ArrayList(); for (URI uri : jar.keySet()) for (HttpCookie cookie : jar.get(uri)) { if (!cookie.hasExpired()) liveCookies.add(cookie); } for (HttpCookie cookie : freeCookies) { if (!cookie.hasExpired()) liveCookies.add(cookie); } return Collections.unmodifiableList(liveCookies); } @Override public List getURIs() { return Collections.unmodifiableList(new ArrayList(jar.keySet())); } @Override public boolean remove(URI uri, HttpCookie cookie) { if (jar.containsKey(uri)) { return jar.get(uri).remove(cookie); } else { return freeCookies.remove(cookie); } } @Override public boolean removeAll() { boolean ret = (jar.size() > 0) || (freeCookies.size() > 0); jar.clear(); freeCookies.clear(); return ret; } } 

因此,如果你有这个CookieJar,那么你可以将ms_cookieJars声明更改为:

 private final static ThreadLocal ms_cookieJars = new ThreadLocal() { @Override protected synchronized CookieStore initialValue() { return new CookieJar(); } }; 

基于这个线程中的答案,我创建了另一个非常简单的ThreadLocalCookieStore实现并将其推送到GitHub (也将其作为Maven依赖项提供):

 public class ThreadLocalCookieStore implements CookieStore { private final static ThreadLocal stores = new ThreadLocal() { @Override protected synchronized CookieStore initialValue() { return (new CookieManager()).getCookieStore(); //InMemoryCookieStore } }; @Override public void add(URI uri, HttpCookie cookie) { getStore().add(uri,cookie); } @Override public List get(URI uri) { return getStore().get(uri); } @Override public List getCookies() { return getStore().getCookies(); } @Override public List getURIs() { return getStore().getURIs(); } @Override public boolean remove(URI uri, HttpCookie cookie) { return getStore().remove(uri,cookie); } @Override public boolean removeAll() { return getStore().removeAll(); } @Override public int hashCode() { return getStore().hashCode(); } protected CookieStore getStore() { return stores.get(); } public void purgeStore() { stores.remove(); } } 

没有太多代码,设置cookie存储变得非常简单,具有任何策略值,例如:

 CookieHandler.setDefault(new java.net.CookieManager( new ThreadLocalCookieStore(), CookiePolicy.ACCEPT_ALL)); 

此外,依赖项还包含一个小型的@WebFilter ,用于在需要时在多个serlvet请求上分隔cookie存储。

您可以为cookie设置不同的路径。 因此它不会被覆盖。

http://docs.oracle.com/javase/6/docs/api/java/net/HttpCookie.html#setPath%28java.lang.String%29

ThreadLocal CookieManager怎么样? 与其他一些答案相同,但似乎需要更少的代码:

 public class ThreadLocalCookies extends CookieManager { private static CookiePolicy s_policy = null; private static ThreadLocal s_impl = new ThreadLocal() { @Override protected CookieManager initialValue() { if (null == s_policy) { throw new IllegalStateException("Call install() first"); } return new CookieManager(null, s_policy); } }; public static void install() { install(CookiePolicy.ACCEPT_ALL); } public static void install(CookiePolicy policy) { s_policy = policy; CookieHandler.setDefault(new ThreadLocalCookies()); } public static void clear() { s_impl.set(new CookieManager(null, s_policy)); } @Override public Map> get(URI uri, Map> requestHeaders) throws IOException { return s_impl.get().get(uri, requestHeaders); } @Override public void put(URI uri, Map> responseHeaders) throws IOException { s_impl.get().put(uri, responseHeaders); } }