Uso de cookies con la biblioteca de volley de Android

¿Alguien sabe cómo adjuntar una cookie de sesión a la solicitud utilizando la biblioteca com.android.volley? Cuando inicio sesión en un sitio web, me da una cookie de sesión. El navegador devolvería esa cookie con cualquier solicitud posterior. Volley no parece hacer eso, al menos no automáticamente.

Gracias.

Volley en realidad no realiza las solicitudes HTTP por sí mismo y, por lo tanto, no administra las cookies directamente. En su lugar, utiliza una instancia de HttpStack para hacer esto. Hay dos implementaciones principales:

  • HurlStack: utiliza HttpUrlConnection bajo el capó
  • HttpClientStack: utiliza Apache HttpClient bajo el capó

La administración de cookies es responsabilidad de esas HttpStacks. Y cada uno maneja las cookies de manera diferente.

Si necesita admitir <2.3, entonces debe usar HttpClientStack:

Configure una instancia de HttpClient y páselo a Volley para que lo use debajo del capó:

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

La ventaja de esto frente a la inserción manual de cookies en los encabezados es que obtiene la administración de cookies real. Las cookies en su tienda responderán correctamente a los controles HTTP que caducan o los actualizan.

He ido un paso más allá y clasifiqué BasicCookieStore para que pueda conservar automáticamente mis cookies en el disco.

¡SIN EMBARGO! Si no necesitas soportar versiones anteriores de Android. Solo usa este método:

 // 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 consultará el CookieManager de forma implícita. HttpUrlConnection también es más eficiente y un poco más limpio para implementar y trabajar con IMO.

vmirinov tiene razón!

Aquí es cómo resolví el problema:

Clase de solicitud:

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

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

El código de transporte HTTP predeterminado para Volley es HttpUrlConnection . Si estoy leyendo la documentación correctamente, debe optar por el soporte automático de cookies de sesión:

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

Consulte también ¿Debería HttpURLConnection con CookieManager manejar automáticamente las cookies de sesión?

La solución @Rastio no funciona si hay múltiples encabezados ‘Set-Cookie’. Envolví el almacén de cookies CookieManager predeterminado y antes de agregar una cookie lo guardé en SharedPreferences usando Gson para serializar la cookie.

Este es un ejemplo de la envoltura del almacén de cookies:

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

Entonces, para usar la tienda de cookies que acaba de configurar en el CookieManager ¡y eso es todo!

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

Los chicos prueban esto en el método de AppController.java de su AppController.java

  CookieHandler.setDefault(new CookieManager()); 

Espero que ahorre tiempo a los desarrolladores. He perdido cuatro horas en la depuración y la búsqueda de la solución adecuada.

Conozco la publicación y soy un poco viejo, pero pasamos por este problema reciente, tenemos que compartir la sesión de un Usuario conectado entre servidores, y la solución del lado del servidor comenzó a requerir que el cliente proporcione un valor a través de la cookie. Una solución que encontramos fue agregar un parámetro al objeto RequestQueue , el fragmento de código en el método getRequestQueue antes de instanciar el RequestQueue encontrado en el siguiente enlace, y resolver el problema, no estoy seguro de cómo, pero comenzó a funcionar.

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

// establecer valor de token

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

Use este método para usar Volley con cookies para:

  1. Utilice solo código muy bien probado con licencia Apache 2
  2. Realice todas las solicitudes que desee al mismo tiempo
  3. Asegúrate de que las cookies persisten en el dispositivo
  4. No tener que reinventar la rueda

Mi servidor usa cookies para autenticarse y, obviamente, quería asegurarme de que las cookies persisten en el dispositivo. Así que mi solución fue utilizar las clases PersistentCookieStore y SerializableCookie desde Asynchronous Http Client para Android .

En primer lugar, para habilitar las solicitudes simultáneas , se necesita un puerto Apache HttpClient v4.3 para Android, uno que viene con el sistema desactualizado. Más información aquí . Yo uso Gradle, así que así es como lo importé:

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

Función para obtener RequestQueue (en mi clase que extiende la Aplicación):

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

Así es como hago una solicitud

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

¡Eso es!

Si ya ha comenzado a implementar su aplicación utilizando la biblioteca de Loopj, notará que no puede usar la nueva instancia de HttpClient en Volley.newRequestQUeue () porque obtendrá varios errores acerca de no cerrar su conexión anterior, etc.

Errores como:

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

Ahora, a veces lleva tiempo refaccionar todas las llamadas a la API anterior y volver a escribir usando voley, pero puedes usar volley y loopj al mismo tiempo y compartir una cookietore entre esas dos hasta que escribas todo en voley (usa voley en lugar de loopj, es mucho mejor 🙂 ).

Así es como puedes compartir HttpClient y CookieStore desde loopj con volley.

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

Esto me pasó a mí, comenzamos a usar loopj. Después de 50 000 líneas de código y descubrimiento de que loopj no siempre funciona como se esperaba, decidimos cambiar a Volley.

La respuesta de @CommonsWare es la que usaría. Sin embargo, parece que KitKat está teniendo algunos errores cuando está hecho (cuando creas un CookieManager con CookieStore personalizado que necesitas si quieres Cookies persistentes). Dado el hecho de que independientemente de la implementación de la CookieStore que se use, Volley lanzaría una NullpointerException , tuve que crear mi propio CookieHandler … usarlo si lo encuentra útil.

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

Esta respuesta no ha sido completamente probada. Usé JSON para serializar Cookies, porque bueno, esa clase no implementa Serializable y es definitiva.

Este es uno de los ejemplos de trabajo más simples. No me gusta el RESTO que no es reparador. Pero creo que todo va como una experiencia de aprendizaje. Lo más importante de notar en el siguiente ejemplo es que curlget.php no tiene session_start. El lenguaje que usas para escribir esa parte depende de ti. En el siguiente ejemplo podría ser cualquier cosa, desde java hasta rust. También podría ubicarse en cualquier lugar.

login.php

Código PHP:

  

curlget.php Código PHP:

  

// Solicitud GET utilizando el SID de la llamada anterior