web-dev-qa-db-fra.com

Utilisation de cookies avec la bibliothèque de volley Android

Quelqu'un sait-il comment attacher un cookie de session à la demande à l'aide de la bibliothèque com.Android.volley? Lorsque je me connecte à un site Web, il me donne un cookie de session. Le navigateur renverrait ce cookie avec toute demande ultérieure. Volley ne semble pas faire cela, du moins pas automatiquement.

Merci.

51
Rastio

vmirinov a raison!

Voici comment j'ai résolu le problème:

Classe de demande:

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

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

}
40
Rastio

En fait, Volley ne fait pas lui-même de requêtes HTTP et ne gère donc pas directement les cookies. Pour cela, il utilise une instance de HttpStack. Il y a deux implémentations principales:

  • HurlStack: Utilise HttpUrlConnection sous le capot
  • HttpClientStack: utilise Apache HttpClient sous le capot

La gestion des cookies est la responsabilité de ces HttpStacks. Et chacun manipule les cookies différemment.

Si vous avez besoin de prendre en charge <2.3, vous devez utiliser le fichier HttpClientStack:

Configurez une instance HttpClient et transmettez-la à Volley pour qu'elle l'utilise sous le capot:

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

L'avantage de cette opération par rapport à l'insertion manuelle de cookies dans les en-têtes est que vous bénéficiez d'une gestion réelle des cookies. Les cookies de votre magasin répondent correctement aux contrôles HTTP qui expirent ou les mettent à jour.

Je suis allé encore plus loin et j'ai classé BasicCookieStore afin de pouvoir automatiquement conserver mes cookies sur le disque.

TOUTEFOIS! Si vous n'avez pas devez prendre en charge les anciennes versions d'Android. Il suffit d'utiliser cette méthode:

// 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 interrogera implicitement le CookieManager. HttpUrlConnection est également plus performant et plus propre à mettre en œuvre et à travailler avec IMO.

57
Adam

Le code de transport HTTP par défaut pour Volley est HttpUrlConnection. Si je lis la documentation correctement, vous devez activer le support des cookies de session automatiques:

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

Voir aussi HttpURLConnection avec CookieManager doit-il gérer automatiquement les cookies de session?

20
CommonsWare

Les gars essayent ceci avec la méthode onCreate de votre AppController.Java 

  CookieHandler.setDefault(new CookieManager());

J'espère que cela fera gagner du temps aux développeurs. J'ai perdu quatre heures à déboguer et à chercher la solution appropriée. 

10
Adnan Bin Mustafa

La solution @Rastio ne fonctionne pas s'il existe plusieurs en-têtes 'Set-Cookie'. J'ai emballé le magasin de cookies CookieManager par défaut et, avant d'ajouter un cookie, je l'ai enregistré dans SharedPreferences en utilisant Gson pour sérialiser le cookie. 

Voici un exemple du wrapper du magasin 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<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();
    }
}

Ensuite, pour utiliser le magasin de cookies qui vient d’être défini dans CookieManager, le tour est joué!

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

Je connais le message et un peu vieux, mais nous avons traversé ce problème récent, nous devons partager la session d'un utilisateur connecté entre les serveurs, et la solution côté serveur a commencé à exiger qu'une valeur soit fournie par le côté client, via cookie. Une solution trouvée consistait à ajouter un paramètre à l’objet RequestQueue, l’extrait de code de la méthode getRequestQueue avant d’instancier la RequestQueue trouvée sur le lien ci-dessous, et à résoudre le problème sans savoir comment, mais cela a commencé à fonctionner.

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

// définir la valeur du jeton

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

Gingerbread Android Versions:

il existe un autre moyen simple de conserver la session des cookies: il s'agit d'ajouter cette ligne à une classe étendue avec la classe APPLICATION:

CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
2
asadullah

Utilisez cette méthode pour utiliser Volley avec les cookies pour:

  1. Utilisez uniquement du code très bien testé sous licence Apache 2
  2. _ {Faites autant de demandes que vous le souhaitez en même temps} _
  3. _ {Assurez-vous que les cookies persistent sur l'appareil)
  4. _ {Ne pas avoir à réinventer la roue} _

Mon serveur utilise des cookies pour s'authentifier et je voulais évidemment m'assurer que les cookies persistent sur l'appareil. Ma solution a donc été d’utiliser PersistentCookieStore et SerializableCookie classes de Client HTTP asynchrone pour Android .

Tout d'abord, pour activer demandes simultanées, un port Apache HttpClient v4.3 pour Android est nécessaire - celui fourni avec le système est obsolète. Plus d'infos ici . J'utilise Gradle, voici comment je l'ai importé:

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

Fonction pour obtenir RequestQueue (dans ma classe qui étend Application):

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

Voici comment je file une demande

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

C'est tout! 

2
C0D3LIC1OU5

Si vous avez déjà commencé à implémenter votre application à l'aide de la bibliothèque Loopj, vous remarquerez que vous ne pouvez pas utiliser la nouvelle instance HttpClient dans Volley.newRequestQUeue () car vous obtiendrez diverses erreurs pour ne pas fermer votre connexion précédente, etc.

Des erreurs comme:

Java.lang.IllegalStateException: No wrapped connection

Invalid use of SingleClientConnManager: connection still allocated.

Maintenant, parfois, il faut du temps pour refactoriser tous vos anciens appels d'API et les réécrire en utilisant volley, mais vous pouvez utiliser volley et loopj en même temps et partager un cookie entre ces deux jusqu'à ce que vous écriviez tout en volley (utilisez volley et non en boucle). c'est beaucoup mieux :)).

Voici comment vous pouvez partager HttpClient et CookieStore de loopj avec 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;
}

Cela m'est arrivé, nous avons commencé à utiliser loopj. Après 50 000 lignes de code et la découverte de cette boucle j ne fonctionne pas toujours comme prévu, nous avons décidé de passer à Volley.

1
Drag0

Dans mon projet, CookieManager est résolu en Android.webkit.CookieManager. Je dois configurer le gestionnaire comme ci-dessous pour que Volley gère automatiquement les cookies.

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

0
starshine wang

La réponse de @CommonsWare est celle que j'utiliserais. Cependant, il semble que KitKat ait quelques bugs quand cela est fait (lorsque vous créez un CookieManager avec la personnalisation CookieStore dont vous avez besoin si vous voulez des cookies persistants) . Compte tenu du fait que quelle que soit la mise en œuvre du CookieStore Volley lancerait un NullpointerException, je devais créer mon propre CookieHandler... l’utiliser si vous le trouvez utile.

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;

}
}

Cette réponse n'a pas été testée à fond. J'ai utilisé JSON pour sérialiser les cookies, car cette classe n'implémente pas Serializable et c'est définitif.

0
caeus