Verwenden von Cookies mit Android-Volley-Bibliothek

Weiß jemand, wie man einen Sitzungscookie an die Anfrage mit der com.android.volley Bibliothek anfügt? Wenn ich mich auf einer Website anmelde, bekomme ich einen Session-Cookie. Der Browser würde das Cookie mit jeder nachfolgenden Anfrage zurücksenden. Volley scheint das nicht zu tun, zumindest nicht automatisch.

Vielen Dank.

Volley stellt keine HTTP-Anfragen selbst und verwaltet Cookies nicht direkt. Es verwendet stattdessen eine Instanz von HttpStack, um dies zu tun. Es gibt zwei Hauptimplementierungen:

  • HurlStack: Verwendet HttpUrlConnection unter der Haube
  • HttpClientStack: verwendet Apache HttpClient unter der Haube

Cookie-Management liegt in der Verantwortung dieser HttpStacks. Und sie behandeln Cookies unterschiedlich.

Wenn Sie <2.3 unterstützen müssen, sollten Sie den HttpClientStack verwenden:

Konfigurieren Sie eine HttpClient-Instanz und übergeben Sie diese an Volley, damit sie unter der Haube verwendet werden kann:

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

Der Vorteil, dass Sie Cookies nicht manuell in die Header einfügen, ist die tatsächliche Cookie-Verwaltung. Cookies in Ihrem Geschäft reagieren ordnungsgemäß auf HTTP-Steuerelemente, die ablaufen oder sie aktualisieren.

Ich bin einen Schritt weiter gegangen und BasicCookieStore unterklassifiziert, so dass ich meine Cookies automatisch auf dem Datenträger beibehalten kann.

JEDOCH! Wenn Sie ältere Versionen von Android nicht unterstützen müssen. Verwenden Sie einfach diese Methode:

 // 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 fragt den CookieManager implizit ab. HttpUrlConnection ist auch leistungsfähiger und ein bisschen sauberer zu implementieren und mit IMO arbeiten.

Vmirinov hat Recht!

Hier ist, wie ich das Problem getriggers habe:

Anfrageklasse:

 public class StringRequest extends com.android.volley.toolbox.StringRequest { private final Map _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 params, Listener listener, ErrorListener errorListener) { super(method, url, listener, errorListener); _params = params; } @Override protected Map getParams() { return _params; } /* (non-Javadoc) * @see com.android.volley.toolbox.StringRequest#parseNetworkResponse(com.android.volley.NetworkResponse) */ @Override protected Response 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 getHeaders() throws AuthFailureError { Map headers = super.getHeaders(); if (headers == null || headers.equals(Collections.emptyMap())) { headers = new HashMap(); } MyApp.get().addSessionCookie(headers); return headers; } } 

und MeineApp:

 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 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 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()); } } } 

Der Standard-HTTP-Transportcode für Volley ist HttpUrlConnection . Wenn ich die Dokumentation richtig lese, müssen Sie sich für die automatische Session-Cookie-Unterstützung entscheiden:

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

Siehe auch Sollen HttpURLConnection mit CookieManager Session-Cookies automatisch verarbeiten?

@Rastio-Lösung funktioniert nicht, wenn mehrere ‘Set-Cookie’-Header vorhanden sind. Ich habe den standardmäßigen CookieManager-Cookie-Speicher umschlossen und vor dem Hinzufügen eines Cookies habe ich ihn in SharedPreferences mit Gson gespeichert, um den Cookie zu serialisieren.

Dies ist ein Beispiel für den Cookie-Speicher-Wrapper:

 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 get(URI uri) { return mStore.get(uri); } @Override public List getCookies() { return mStore.getCookies(); } @Override public List getURIs() { return mStore.getURIs(); } @Override public boolean remove(URI uri, HttpCookie cookie) { return mStore.remove(uri, cookie); } @Override public boolean removeAll() { return mStore.removeAll(); } } 

Dann, um den Cookie-Speicher nur im CookieManager gesetzt zu verwenden und das war’s!

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

Jungs versuchen dies in onCreate Methode von Ihrem AppController.java

  CookieHandler.setDefault(new CookieManager()); 

Ich hoffe, es wird Zeit für Entwickler sparen. Ich habe vier Stunden mit der Fehlersuche und der Suche nach geeigneten Lösungen verschwendet.

Ich kenne den Beitrag und ein wenig alt, aber wir gingen durch dieses jüngste Problem, wir müssen die Sitzung eines protokollierten Benutzers zwischen Servern teilen, und serverseitige Lösung begann, einen Wert zu verlangen, um von der Clientseite durch Cookie zur Verfügung gestellt werden. Eine Lösung, die wir gefunden haben, bestand darin, dem RequestQueue Objekt, dem Code-Snippet in der Methode getRequestQueue , einen Parameter RequestQueue , bevor die RequestQueue auf dem folgenden Link instanziiert wird, und das Problem zu lösen, nicht sicher, wie es funktioniert, aber es hat begonnen zu arbeiten.

Besuchen Sie 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  void addToRequestQueue( final Request req ) { req.setTag( App.TAG ); this.getRequestQueue().add( req ); } public  void addToRequestQueue( final Request 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; } } 

// Token-Wert setzen

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

Verwenden Sie diese Methode, um Volley mit Cookies zu verwenden, um:

  1. Verwenden Sie nur sehr gut getesteten Code, der unter Apache 2 lizenziert ist
  2. Machen Sie so viele Anfragen wie Sie möchten gleichzeitig
  3. Stellen Sie sicher, dass Cookies auf dem Gerät bestehen bleiben
  4. Das Rad nicht neu erfinden müssen

Mein Server verwendet Cookies zur Authentifizierung und ich wollte natürlich sicherstellen, dass Cookies auf dem Gerät bestehen bleiben. Meine Lösung bestand also darin, PersistentCookieStore und SerializableCookie- classn vom Asynchronous Http Client für Android zu verwenden .

Erstens, um gleichzeitige Anfragen zu ermöglichen, wird ein Apache HttpClient v4.3-Port für Android benötigt – einer, der mit dem System geliefert wird, ist veraltet. Mehr Infos hier . Ich benutze Gradle, darum habe ich es importiert:

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

function zum Abrufen von RequestQueue (in meiner class, die Application erweitert):

 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; } 

So stelle ich eine Anfrage an

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

Das ist es!

Wenn Sie bereits begonnen haben, Ihre Anwendung mithilfe der Loopj-Bibliothek zu implementieren, werden Sie feststellen, dass Sie keine neue HttpClient-Instanz in Volley.newRequestQUeue () verwenden können, da Sie verschiedene Fehler bekommen, wenn Sie Ihre vorherige Verbindung nicht schließen usw.

Fehler wie:

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

Jetzt braucht es manchmal Zeit, all deine alten API-Aufrufe umzuformen und sie mit Volley neu zu schreiben, aber du kannst gleichzeitig Volley und Loopj benutzen und einen Cookiestore zwischen diesen beiden teilen, bis du alles in Volley geschrieben hast (benutze Volley anstelle von Loopj) ist viel besser 🙂 ).

So können Sie HttpClient und CookieStore von loopj mit Volley teilen.

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

Das ist mir passiert, wir haben angefangen, loopj zu benutzen. Nach 50 000 Codezeilen und der Entdeckung, dass loopj nicht immer wie erwartet funktioniert, haben wir uns entschieden, zu Volley zu wechseln.

Die Antwort von @CommonsWare ist diejenige, die ich verwenden würde. Es sieht jedoch so aus, als hätte KitKat einige Fehler, wenn das erledigt ist (Wenn Sie einen CookieManager mit einem benutzerdefinierten CookieStore den Sie benötigen, wenn Sie dauerhafte Cookies wünschen). Angesichts der Tatsache, dass Volley unabhängig von der Implementierung des CookieStore eine NullpointerException , musste ich meinen eigenen CookieHandler erstellen … benutze ihn, wenn du ihn hilfreich findest.

 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> 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>(); Iterator jsonurisiter = jsonuris.keys(); while (jsonurisiter.hasNext()) { String prop = jsonurisiter.next(); HashMap cookiesMap = new HashMap(); JSONObject jsoncookies = jsonuris.getJSONObject(prop); Iterator 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>(); } } @Override public Map> get(URI arg0, Map> arg1) throws IOException { Log.d("MyCookieHandler.get", "getting Cookies for domain: " + arg0.getHost()); Map cookies = urisMap.get(arg0.getHost()); if (cookies != null) for (Entry 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> arg1) throws IOException { Map 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 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> uris : urisMap .entrySet()) { JSONObject jsoncookies = new JSONObject(); for (Entry 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 parseCookies(Map> map) { Map response = new HashMap(); for (Entry> 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; } } 

Diese Antwort wurde nicht gründlich getestet. Ich habe JSON verwendet, um Cookies zu serialisieren, denn diese class implementiert Serializable und es ist endgültig.

Hier ist eines der einfachsten Arbeitsbeispiele. Ich mag REST nicht, die nicht erholsam ist. Aber ich denke, alles ist eine Lernerfahrung. Das Wichtigste im folgenden Beispiel ist, dass curlget.php session_start nicht hat. Welche Sprache Sie verwenden, um diesen Teil zu schreiben, hängt von Ihnen ab. Im folgenden Beispiel könnte alles von Java bis Rost sein. Es könnte auch irgendwo finden.

login.php

PHP-Code:

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

cellget.php PHP-Code:

 < ?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); 

// GET-Anfrage mit der SID des vorherigen Anrufs