使用Cookie与Android排球库

有谁知道如何使用com.android.volley库将会话cookie附加到请求? 当我login到一个网站,它给了我一个会话cookie。 浏览器会将该cookie发送回任何后续请求。 似乎似乎不这样做,至less不会自动。

谢谢。

Volley本身并不实际发出HTTP请求,因此不直接pipe理Cookies。 而是使用HttpStack的一个实例来做到这一点。 有两个主要的实现:

  • HurlStack:引擎盖下使用HttpUrlConnection
  • HttpClientStack:引擎盖下使用Apache HttpClient

Cookiepipe理是这些HttpStack的责任。 他们每个人都不同地处理Cookie。

如果你需要支持<2.3,那么你应该使用HttpClientStack:

configuration一个HttpClient实例,并将其传递给Volley以便在其下使用:

 // If you need to directly manipulate cookies later on, hold onto this client // object as it gives you access to the Cookie Store DefaultHttpClient httpclient = new DefaultHttpClient(); CookieStore cookieStore = new BasicCookieStore(); httpclient.setCookieStore( cookieStore ); HttpStack httpStack = new HttpClientStack( httpclient ); RequestQueue requestQueue = Volley.newRequestQueue( context, httpStack ); 

这个vs手动插入cookie的好处是你可以得到实际的cookiepipe理。 您商店中的Cookie将正确响应过期或更新它们的HTTP控件。

我已经更进了一步,并对BasicCookieStore进行了分类,以便我可以自动将Cookie保存到磁盘。

然而! 如果你不需要支持旧版本的Android。 只要使用这个方法:

 // CookieStore is just an interface, you can implement it and do things like // save the cookies to disk or what ever. CookieStore cookieStore = new MyCookieStore(); CookieManager manager = new CookieManager( cookieStore, CookiePolicy.ACCEPT_ALL ); CookieHandler.setDefault( manager ); // Optionally, you can just use the default CookieManager CookieManager manager = new CookieManager(); CookieHandler.setDefault( manager ); 

HttpURLConnection将隐式地从中查询CookieManager。 HttpUrlConnection也是更高性能,并且稍微清洁一些,可以实现和使用IMO。

vmirinov是正确的!

这是我解决问题的方法:

请求类:

 public class StringRequest extends com.android.volley.toolbox.StringRequest { private final Map<String, String> _params; /** * @param method * @param url * @param params * A {@link HashMap} to post with the request. Null is allowed * and indicates no parameters will be posted along with request. * @param listener * @param errorListener */ public StringRequest(int method, String url, Map<String, String> params, Listener<String> listener, ErrorListener errorListener) { super(method, url, listener, errorListener); _params = params; } @Override protected Map<String, String> getParams() { return _params; } /* (non-Javadoc) * @see com.android.volley.toolbox.StringRequest#parseNetworkResponse(com.android.volley.NetworkResponse) */ @Override protected Response<String> parseNetworkResponse(NetworkResponse response) { // since we don't know which of the two underlying network vehicles // will Volley use, we have to handle and store session cookies manually MyApp.get().checkSessionCookie(response.headers); return super.parseNetworkResponse(response); } /* (non-Javadoc) * @see com.android.volley.Request#getHeaders() */ @Override public Map<String, String> getHeaders() throws AuthFailureError { Map<String, String> headers = super.getHeaders(); if (headers == null || headers.equals(Collections.emptyMap())) { headers = new HashMap<String, String>(); } MyApp.get().addSessionCookie(headers); return headers; } } 

和MyApp:

 public class MyApp extends Application { private static final String SET_COOKIE_KEY = "Set-Cookie"; private static final String COOKIE_KEY = "Cookie"; private static final String SESSION_COOKIE = "sessionid"; private static MyApp _instance; private RequestQueue _requestQueue; private SharedPreferences _preferences; public static MyApp get() { return _instance; } @Override public void onCreate() { super.onCreate(); _instance = this; _preferences = PreferenceManager.getDefaultSharedPreferences(this); _requestQueue = Volley.newRequestQueue(this); } public RequestQueue getRequestQueue() { return _requestQueue; } /** * Checks the response headers for session cookie and saves it * if it finds it. * @param headers Response Headers. */ public final void checkSessionCookie(Map<String, String> headers) { if (headers.containsKey(SET_COOKIE_KEY) && headers.get(SET_COOKIE_KEY).startsWith(SESSION_COOKIE)) { String cookie = headers.get(SET_COOKIE_KEY); if (cookie.length() > 0) { String[] splitCookie = cookie.split(";"); String[] splitSessionId = splitCookie[0].split("="); cookie = splitSessionId[1]; Editor prefEditor = _preferences.edit(); prefEditor.putString(SESSION_COOKIE, cookie); prefEditor.commit(); } } } /** * Adds session cookie to headers if exists. * @param headers */ public final void addSessionCookie(Map<String, String> headers) { String sessionId = _preferences.getString(SESSION_COOKIE, ""); if (sessionId.length() > 0) { StringBuilder builder = new StringBuilder(); builder.append(SESSION_COOKIE); builder.append("="); builder.append(sessionId); if (headers.containsKey(COOKIE_KEY)) { builder.append("; "); builder.append(headers.get(COOKIE_KEY)); } headers.put(COOKIE_KEY, builder.toString()); } } } 

Volley的默认HTTP传输代码是HttpUrlConnection 。 如果我正确阅读文档 ,则需要select自动会话Cookie支持:

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

另请参见应Cookie的HttpURLConnection自动处理会话Cookie?

如果有多个“Set-Cookie”标题,则@Rastio解决scheme不起作用。 我包装了默认的CookieManager cookie存储,在添加一个cookie之前,我使用Gson将它保存在SharedPreferences中以序列化cookie。

这是一个cookie存储包装的例子:

 import android.content.Context; import android.net.Uri; import android.util.Log; import com.google.gson.Gson; import java.net.CookieManager; import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; import java.util.List; /** * Class that implements CookieStore interface. This class saves to SharedPreferences the session * cookie. * * Created by lukas. */ public class PersistentCookieStore implements CookieStore { private CookieStore mStore; private Context mContext; private Gson mGson; public PersistentCookieStore(Context context) { // prevent context leaking by getting the application context mContext = context.getApplicationContext(); mGson = new Gson(); // get the default in memory store and if there is a cookie stored in shared preferences, // we added it to the cookie store mStore = new CookieManager().getCookieStore(); String jsonSessionCookie = Prefs.getJsonSessionCookie(mContext); if (!jsonSessionCookie.equals(Prefs.DEFAULT_STRING)) { HttpCookie cookie = mGson.fromJson(jsonSessionCookie, HttpCookie.class); mStore.add(URI.create(cookie.getDomain()), cookie); } } @Override public void add(URI uri, HttpCookie cookie) { if (cookie.getName().equals("sessionid")) { // if the cookie that the cookie store attempt to add is a session cookie, // we remove the older cookie and save the new one in shared preferences remove(URI.create(cookie.getDomain()), cookie); Prefs.saveJsonSessionCookie(mContext, mGson.toJson(cookie)); } mStore.add(URI.create(cookie.getDomain()), cookie); } @Override public List<HttpCookie> get(URI uri) { return mStore.get(uri); } @Override public List<HttpCookie> getCookies() { return mStore.getCookies(); } @Override public List<URI> getURIs() { return mStore.getURIs(); } @Override public boolean remove(URI uri, HttpCookie cookie) { return mStore.remove(uri, cookie); } @Override public boolean removeAll() { return mStore.removeAll(); } } 

然后,使用刚才在CookieManager中设置的cookie存储,就是这样!

 CookieManager cookieManager = new CookieManager(new PersistentCookieStore(mContext), CookiePolicy.ACCEPT_ORIGINAL_SERVER); CookieHandler.setDefault(cookieManager); 

你试试AppController.java onCreate方法

  CookieHandler.setDefault(new CookieManager()); 

希望这会节省开发人员的时间。 我已经浪费了四个小时来debugging和寻找合适的解决scheme。

我知道这个post有点老了,但是我们经历了这个最近的问题,我们需要在服务器之间共享一个logging用户的会话,而服务器端的解决scheme开始要求客户端通过cookie提供一个值。 我们发现的一个解决scheme是在RequestQueue对象的getRequestQueue方法中添加一个参数,然后在下面的链接上实例化RequestQueue ,并解决问题,不知道如何,但是它开始工作。

访问http://woxiangbo.iteye.com/blog/1769122

 public class App extends Application { public static final String TAG = App.class.getSimpleName(); private static App mInstance; public static synchronized App getInstance() { return App.mInstance; } private RequestQueue mRequestQueue; public <T> void addToRequestQueue( final Request<T> req ) { req.setTag( App.TAG ); this.getRequestQueue().add( req ); } public <T> void addToRequestQueue( final Request<T> req, final String tag ) { req.setTag( TextUtils.isEmpty( tag ) ? App.TAG : tag ); this.getRequestQueue().add( req ); } public void cancelPendingRequests( final Object tag ) { if ( this.mRequestQueue != null ) { this.mRequestQueue.cancelAll( tag ); } } public RequestQueue getRequestQueue() { if ( this.mRequestQueue == null ) { DefaultHttpClient mDefaultHttpClient = new DefaultHttpClient(); final ClientConnectionManager mClientConnectionManager = mDefaultHttpClient.getConnectionManager(); final HttpParams mHttpParams = mDefaultHttpClient.getParams(); final ThreadSafeClientConnManager mThreadSafeClientConnManager = new ThreadSafeClientConnManager( mHttpParams, mClientConnectionManager.getSchemeRegistry() ); mDefaultHttpClient = new DefaultHttpClient( mThreadSafeClientConnManager, mHttpParams ); final HttpStack httpStack = new HttpClientStack( mDefaultHttpClient ); this.mRequestQueue = Volley.newRequestQueue( this.getApplicationContext(), httpStack ); } return this.mRequestQueue; } @Override public void onCreate() { super.onCreate(); App.mInstance = this; } } 

//设置标记值

 ObjectRequest.setHeader( "Cookie", "JSESSIONID=" + tokenValueHere ); 

使用这种方法使用曲奇的Volley来:

  1. 只能使用经过Apache 2许可授权的经过良好testing的代码
  2. 尽可能多的请求,你想在同一时间
  3. 确保Cookie保留在设备上
  4. 不必重新发明轮子

我的服务器使用cookie进行身份validation,显然我想确保cookie保留在设备上。 所以我的解决scheme是使用Android的asynchronousHttp客户端的 PersistentCookieStore和SerializableCookie类。

首先,为了启用并发请求 ,需要一个用于Android的Apache HttpClient v4.3端口 – 系统自带的一个已过时。 更多信息在这里 。 我使用Gradle,所以这是我如何导入它:

 dependencies { compile group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.3' } 

函数来获取RequestQueue(在我的类扩展应用程序):

 private RequestQueue mRequestQueue; private CloseableHttpClient httpClient; 

 public RequestQueue getRequestQueue() { if (mRequestQueue == null) { httpClient = HttpClients.custom() .setConnectionManager(new PoolingHttpClientConnectionManager()) .setDefaultCookieStore(new PersistentCookieStore(getApplicationContext())) .build(); mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new HttpClientStack(httpClient)); } return mRequestQueue; } 

这是我如何排队请求

 public <T> void addToRequestQueue(Request<T> req, String tag) { req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req); } 

而已!

如果您已经开始使用Loopj库实现您的应用程序,您将会注意到您不能在Volley.newRequestQUeue()中使用新的HttpClient实例,因为您会收到关于不closures以前的连接等的各种错误。

错误如下:

 java.lang.IllegalStateException: No wrapped connection Invalid use of SingleClientConnManager: connection still allocated. 

现在,有时候需要时间来重构所有旧的API调用,并使用volley重写它们,但是您可以同时使用volley和loopj,并在这两者之间共享一个cookiestore,直到您将所有内容写入齐次(使用volley而不是loopj,是好得多:))。

这就是你可以如何从volj与loopj共享HttpClient和CookieStore。

 // For example you initialize loopj first private static AsyncHttpClient client = new AsyncHttpClient(); sCookieStore = new PersistentCookieStore(getSomeContextHere()); client.setTimeout(DEFAULT_TIMEOUT); client.setMaxConnections(12); client.setCookieStore(sCookieStore); client.setThreadPool(((ThreadPoolExecutor) Executors.newCachedThreadPool())); public static RequestQueue getRequestQueue(){ if(mRequestQueue == null){ HttpClient httpclient = KkstrRestClient.getClient().getHttpClient(); ((AbstractHttpClient) httpclient).setCookieStore( ApplicationController.getCookieStore() ); HttpStack httpStack = new HttpClientStack(httpclient); mRequestQueue = Volley.newRequestQueue(getContext(), httpStack); } return mRequestQueue; } 

这发生在我身上,我们开始使用loopj。 经过5万行代码和发现loopj并不总是像预期的那样工作,我们决定切换到Volley。

@CommonsWare的答案是我会使用的。 然而,看起来像KitKat有一些错误,当这样做(当你创build一个CookieManager的定制CookieStore ,你需要如果你想持久性Cookies)。 鉴于事实上,无论使用的CookieStore的实施,Volley会抛出一个NullpointerException ,我不得不创build我自己的CookieHandler …使用它,如果你觉得有帮助。

 public class MyCookieHandler extends CookieHandler { private static final String VERSION_ZERO_HEADER = "Set-cookie"; private static final String VERSION_ONE_HEADER = "Set-cookie2"; private static final String COOKIE_HEADER = "Cookie"; private static final String COOKIE_FILE = "Cookies"; private Map<String, Map<String, HttpCookie>> urisMap; private Context context; public MyCookieHandler(Context context) { this.context = context; loadCookies(); } @SuppressWarnings("unchecked") private void loadCookies() { File file = context.getFileStreamPath(COOKIE_FILE); if (file.exists()) try { FileInputStream fis = context.openFileInput(COOKIE_FILE); BufferedReader br = new BufferedReader(new InputStreamReader( fis)); String line = br.readLine(); StringBuilder sb = new StringBuilder(); while (line != null) { sb.append(line); line = br.readLine(); } Log.d("MyCookieHandler.loadCookies", sb.toString()); JSONObject jsonuris = new JSONObject(sb.toString()); urisMap = new HashMap<String, Map<String, HttpCookie>>(); Iterator<String> jsonurisiter = jsonuris.keys(); while (jsonurisiter.hasNext()) { String prop = jsonurisiter.next(); HashMap<String, HttpCookie> cookiesMap = new HashMap<String, HttpCookie>(); JSONObject jsoncookies = jsonuris.getJSONObject(prop); Iterator<String> jsoncookiesiter = jsoncookies.keys(); while (jsoncookiesiter.hasNext()) { String pprop = jsoncookiesiter.next(); cookiesMap.put(pprop, jsonToCookie(jsoncookies.getJSONObject(pprop))); } urisMap.put(prop, cookiesMap); } } catch (Exception e) { e.printStackTrace(); } else { urisMap = new HashMap<String, Map<String, HttpCookie>>(); } } @Override public Map<String, List<String>> get(URI arg0, Map<String, List<String>> arg1) throws IOException { Log.d("MyCookieHandler.get", "getting Cookies for domain: " + arg0.getHost()); Map<String, HttpCookie> cookies = urisMap.get(arg0.getHost()); if (cookies != null) for (Entry<String, HttpCookie> cookie : cookies.entrySet()) { if (cookie.getValue().hasExpired()) { cookies.remove(cookie.getKey()); } } if (cookies == null || cookies.isEmpty()) { Log.d("MyCookieHandler.get", "======"); return Collections.emptyMap(); } Log.d("MyCookieHandler.get", "Cookie : " + TextUtils.join("; ", cookies.values())); Log.d("MyCookieHandler.get", "======"); return Collections.singletonMap(COOKIE_HEADER, Collections .singletonList(TextUtils.join("; ", cookies.values()))); } @Override public void put(URI uri, Map<String, List<String>> arg1) throws IOException { Map<String, HttpCookie> cookies = parseCookies(arg1); Log.d("MyCookieHandler.put", "saving Cookies for domain: " + uri.getHost()); addCookies(uri, cookies); Log.d("MyCookieHandler.put", "Cookie : " + TextUtils.join("; ", cookies.values())); Log.d("MyCookieHandler.put", "======"); } private void addCookies(URI uri, Map<String, HttpCookie> cookies) { if (!cookies.isEmpty()) { if (urisMap.get(uri.getHost()) == null) { urisMap.put(uri.getHost(), cookies); } else { urisMap.get(uri.getHost()).putAll(cookies); } saveCookies(); } } private void saveCookies() { try { FileOutputStream fos = context.openFileOutput(COOKIE_FILE, Context.MODE_PRIVATE); JSONObject jsonuris = new JSONObject(); for (Entry<String, Map<String, HttpCookie>> uris : urisMap .entrySet()) { JSONObject jsoncookies = new JSONObject(); for (Entry<String, HttpCookie> savedCookies : uris.getValue() .entrySet()) { jsoncookies.put(savedCookies.getKey(), cookieToJson(savedCookies.getValue())); } jsonuris.put(uris.getKey(), jsoncookies); } fos.write(jsonuris.toString().getBytes()); fos.close(); Log.d("MyCookieHandler.addCookies", jsonuris.toString()); } catch (Exception e) { e.printStackTrace(); } } private static JSONObject cookieToJson(HttpCookie cookie) { JSONObject jsoncookie = new JSONObject(); try { jsoncookie.put("discard", cookie.getDiscard()); jsoncookie.put("maxAge", cookie.getMaxAge()); jsoncookie.put("secure", cookie.getSecure()); jsoncookie.put("version", cookie.getVersion()); jsoncookie.put("comment", cookie.getComment()); jsoncookie.put("commentURL", cookie.getCommentURL()); jsoncookie.put("domain", cookie.getDomain()); jsoncookie.put("name", cookie.getName()); jsoncookie.put("path", cookie.getPath()); jsoncookie.put("portlist", cookie.getPortlist()); jsoncookie.put("value", cookie.getValue()); } catch (JSONException e) { e.printStackTrace(); } return jsoncookie; } private static HttpCookie jsonToCookie(JSONObject jsonObject) { HttpCookie httpCookie; try { httpCookie = new HttpCookie(jsonObject.getString("name"), jsonObject.getString("value")); if (jsonObject.has("comment")) httpCookie.setComment(jsonObject.getString("comment")); if (jsonObject.has("commentURL")) httpCookie.setCommentURL(jsonObject.getString("commentURL")); if (jsonObject.has("discard")) httpCookie.setDiscard(jsonObject.getBoolean("discard")); if (jsonObject.has("domain")) httpCookie.setDomain(jsonObject.getString("domain")); if (jsonObject.has("maxAge")) httpCookie.setMaxAge(jsonObject.getLong("maxAge")); if (jsonObject.has("path")) httpCookie.setPath(jsonObject.getString("path")); if (jsonObject.has("portlist")) httpCookie.setPortlist(jsonObject.getString("portlist")); if (jsonObject.has("secure")) httpCookie.setSecure(jsonObject.getBoolean("secure")); if (jsonObject.has("version")) httpCookie.setVersion(jsonObject.getInt("version")); return httpCookie; } catch (JSONException e) { e.printStackTrace(); } return null; } private Map<String, HttpCookie> parseCookies(Map<String, List<String>> map) { Map<String, HttpCookie> response = new HashMap<String, HttpCookie>(); for (Entry<String, List<String>> e : map.entrySet()) { String key = e.getKey(); if (key != null && (key.equalsIgnoreCase(VERSION_ONE_HEADER) || key .equalsIgnoreCase(VERSION_ZERO_HEADER))) { for (String cookie : e.getValue()) { try { for (HttpCookie htpc : HttpCookie.parse(cookie)) { response.put(htpc.getName(), htpc); } } catch (Exception e1) { Log.e("MyCookieHandler.parseCookies", "Error parsing cookies", e1); } } } } return response; } } 

这个答案还没有经过彻底的testing。 我用JSON来序列化Cookies,因为那个类没有实现Serializable ,它是最终的。

这是最简单的工作例子之一。 我不喜欢不rest的REST。 但我想所有事情都是作为一种学习经验。 在下面的例子中最重要的是curlget.php没有session_start。 你用什么语言写这部分是由你决定的。 在下面的例子中,它可以是任何从Java到生锈。 它也可以在任何地方find。

的login.php

PHP代码:

 <?php session_start(); //we store any POST/GET request to session $_SESSION['un'] = $_REQUEST['username']; userinfo.php PHP Code: <?php session_start(); echo json_encode($_SESSION['un']); 

curlget.php PHP代码:

 <?php $ch = curl_init(); //we do login and get the session_id in from it's responce headers curl_setopt($ch, CURLOPT_URL,"http://localhost/login.php"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS,"username=test"); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec ($ch); //we get the cookie from response header with the hardest possible way //most platforms have tools for these preg_match_all('/^Set-Cookie:\s*([^\r\n]*)/mi', $result, $ms); $cookies = array(); foreach ($ms[1] as $m) { list($name, $value) = explode('=', $m, 2); $cookies[$name] = $value; } $parts = explode(";",$cookies['PHPSESSID']); //The SessionId finally $SID = $parts[0]; curl_close ($ch); 

//使用之前调用的SID获取请求