httpclient 多线程与cookie
in java on cookie, httpclient - Hits()
HttpClient4.2之后,PoolingClientConnectionManager说是实现了池的连接管理,也是线程安全的,这个对于cookie来说则不是。
场景:
请求同一个链接,但是需要以不同的session 来请求,那么使用PoolingClientConnectionManager产生的同一个httpclient去访问,会使用相同的cookie。
PoolingClientConnectionManager connManager = new PoolingClientConnectionManager( registry); httpClient = new DefaultHttpClient(connManager, params); return httpClient;
向上面的代码,一般是公用这个httpclient, 这样做对于cookie来说就不是多线程安全的了。即使你每次设置了cookie header,也是存在这个问题。这个并不是PoolingClientConnectionManager的原因,而是共用了httpclient造成的。
解决方案:
使用BasicClientConnectionManager,并且每次都创建httpclient,因为每次都创建httpclient,当然就没必要使用复杂的PoolingClientConnectionManager。
每次登录请求后response.getHeaders("Set-Cookie");获得cookie Header[]
再次请求时将cookie 加入请求header即可。
上面的方法对于一般场景可以,但是今天我测试登陆j_spring_security_check这样的验证则不行,还是参照这里
http://codeblow.com/questions/how-do-you-connect-with-an-online-url-which-requires-spring-security/
其窍门是,先要获取一次取得原始cookie,然后第二次请求登陆链接,传入登陆参数,第三次再请求要访问的页面。这三次请求要使用同一个httpclient(非poolmanager)。
我作了修改写了个公用函数,考虑到有些情况需要登出,在完成获取数据后再登出,一共是四步,真是纠结的方案……
public static String getWithLogin(String loginUrl,String url,String logoutUrl,Map map) {
DefaultHttpClient httpclient = (DefaultHttpClient) HttpClientUtils
.getHttpClient(true);
try {
//init cookie
HttpGet httpget = new HttpGet(url);
HttpResponse httpClientResponse = httpclient.execute(httpget);
HttpEntity entity = httpClientResponse.getEntity();
EntityUtils.consume(entity);
//login
HttpPost httppost = new HttpPost(loginUrl);
// Prepare post parameters
if (null != map && !map.isEmpty()){
Iterator it = map.entrySet().iterator();
List<NameValuePair> params = new LinkedList<NameValuePair>();
while (it.hasNext()) {
Map.Entry en = (Map.Entry) it.next();
if (null == en.getValue()) {
continue;
}
String key = en.getKey().toString();
String value = en.getValue().toString();
params.add(new BasicNameValuePair(key, value));
}
httppost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
}
httpClientResponse = httpclient.execute(httppost);
//check if logined
// check response body
entity = httpClientResponse.getEntity();
String result = EntityUtils.toString(entity);
EntityUtils.consume(entity);
int statusCode = httpClientResponse.getStatusLine().getStatusCode();
if (200 != statusCode && 302 != statusCode){
throw new RuntimeException(statusCode + ":" + result);
}
//get data
httpget = new HttpGet(url);
httpClientResponse = httpclient.execute(httpget);
entity = httpClientResponse.getEntity();
result = EntityUtils.toString(entity);
EntityUtils.consume(entity);
statusCode = httpClientResponse.getStatusLine().getStatusCode();
//logout
if (null != logoutUrl){
try {
httpget = new HttpGet(logoutUrl);
httpClientResponse = httpclient.execute(httpget);
entity = httpClientResponse.getEntity();
EntityUtils.consume(entity);
} catch (Exception e) {
Log.warn(e,"log out fail to" + logoutUrl);
}
}
if (200 != statusCode){
throw new RuntimeException(statusCode + ":" + result);
}
return result;
} catch (Exception e) {
throw ExceptionUtils.silence(e);
} finally {
httpclient.getConnectionManager().shutdown();
}
}
