Problème de cookie Android WebView

89

J'ai un serveur qui envoie à mon application Android un cookie de session à utiliser pour une communication authentifiée. J'essaye de charger une WebView avec une URL pointant vers ce même serveur et j'essaye de passer le cookie de session pour l'authentification. J'observe que cela fonctionne par intermittence mais je n'ai aucune idée pourquoi. J'utilise le même cookie de session pour effectuer d'autres appels sur mon serveur et ceux-ci n'échouent jamais à l'authentification. Je n'observe ce problème qu'en essayant de charger une URL dans une WebView, et cela ne se produit pas à chaque fois. Très frustrant.

Voici le code que j'utilise pour ce faire. Toute aide sera fortement appréciée.

String myUrl = ""http://mydomain.com/"; 
CookieSyncManager.createInstance(this); 
CookieManager cookieManager = CookieManager.getInstance(); 
Cookie sessionCookie =  getCookie(); 
if(sessionCookie != null){ 
    String cookieString = sessionCookie.getName() +"="+sessionCookie.getValue()+"; domain="+sessionCookie.getDomain(); 
    cookieManager.setCookie(myUrl, cookieString); 
    CookieSyncManager.getInstance().sync(); 
} 

WebView webView = (WebView) findViewById(R.id.webview); 
webView.getSettings().setBuiltInZoomControls(true); 
webView.getSettings().setJavaScriptEnabled(true); 
webView.setWebViewClient(new MyWebViewClient()); 
webView.loadUrl(myUrl);
nannerpus
la source
référez-vous à cette question stackoverflow.com/questions/2566485/…
neeraj t

Réponses:

53

Merci justingrammens ! Cela a fonctionné pour moi, j'ai réussi à partager le cookie dans mes requêtes DefaultHttpClient et l'activité WebView:

//------- Native request activity
private DefaultHttpClient httpClient;
public static Cookie cookie = null;

//After Login
List<Cookie> cookies = httpClient.getCookieStore().getCookies();
for (int i = 0; i < cookies.size(); i++) {
    cookie = cookies.get(i);
}

//------- Web Browser activity
Cookie sessionCookie = myapp.cookie;
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
if (sessionCookie != null) {
    cookieManager.removeSessionCookie();
    String cookieString = sessionCookie.getName() + "=" + sessionCookie.getValue() + "; domain=" + sessionCookie.getDomain();
    cookieManager.setCookie(myapp.domain, cookieString);
    CookieSyncManager.getInstance().sync();
}   
k7k0
la source
Cela a très bien fonctionné pour moi. J'ai construit mon url de cookie ainsi: String url = (cookie.isSecure ()? "Https": "http") + ": //" + cookie.getDomain () + cookie.getPath ();
Heath Borders
merci pour le message ... cela m'a aidé à implémenter la déconnexion de Twitter pour mon application ...;)
rahul
pouvez-vous dire ce qui doit être écrit à la place de myapp.cookie
suraj jain
le mot clé est: String cookieString = sessionCookie.getName () + "=" + sessionCookie.getValue () + "; domain =" + sessionCookie.getDomain (); cookieManager.setCookie (myapp.domain, cookieString);
Zennichimaro
1
CookieSyncManager est obsolète maintenant :(
Misha Akopov
18

Merci Android d'avoir gâché mon dimanche. . . Voici ce qui a corrigé mes applications (après avoir lancé votre webview)

if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) {

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

Je devrais dire que les réponses ci-dessus fonctionneront probablement, mais dans ma situation, au moment où Android est passé à la v5 +, mes `` applications '' javascript webview android sont mortes.

Jody Jacobus Geers
la source
1
Oh! Vous venez de sauver ma journée!
Le Minh
14

Solution: Webview CookieSyncManager

CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(mWebView.getContext());
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();
cookieManager.setCookie("http://xx.example.com","mid="+MySession.GetSession().sessionId+" ; Domain=.example.com");
cookieSyncManager.sync();

String cookie = cookieManager.getCookie("http://xx.example.com");

Log.d(LOGTAG, "cookie ------>"+cookie);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(new TuWebViewClient());
mWebView.loadUrl("http://xx.example.com");
Sanket
la source
qu'est-ce que vous obtenez cookie?, je ne reçois que: PHPSESSID=ljfakdjfklasdfaj!, est-ce suffisant?
Francisco Corrales Morales
6
Qu'est-ce que MySession ici?
User3
3

Je voudrais enregistrer ce cookie de session comme préférence et le repeupler avec force dans le gestionnaire de cookies. Il semble que le cookie de session ne survit pas au redémarrage de l'activité

Bostone
la source
Je dois ajouter que mon application effectue de nombreux autres appels non WebView sur mon serveur qui n'échouent jamais à l'authentification. Ce n'est que lorsque j'essaye de charger une URL dans une WebView que je remarque ce problème. "Cookie sessionCookie = getCookie ();" récupère à partir de la base de données de l'application le cookie de session que j'utilise pour tous les messages avec mon serveur.
nannerpus
Eh bien, si vous utilisez HttpClient pour cela, il a son propre magasin de cookies, donc si vous vous en tenez à l'instance unique du client, votre cookie survivra, mais il peut n'avoir rien à voir avec celui utilisé par votre vue Web
Bostone
Si je vous comprends bien, vous dites que le CookieManager renvoyé par CookieManager.getInstance (); affectera les cookies utilisés par les instances de HttpClient, que les WebViews n'utilisent pas. Si tel est le cas, savez-vous comment je peux transmettre explicitement des cookies dans WebViews? Aussi, en regardant la documentation de CookieManager, peut-être que ne pas appeler "cookieManager.setAcceptCookie (true)" me pose des problèmes? Merci pour l'aide, je l'apprécie vraiment.
nannerpus
3

J'ai passé la plus grande moitié de 3 heures à travailler sur un problème très similaire. Dans mon cas, j'ai eu un certain nombre d'appels que j'ai passés à un service Web en utilisant un DefaulHttpClient, puis j'ai voulu définir la session et tous les autres cookies correspondants dans mon WebView.

Je ne sais pas si cela résoudra votre problème, car je ne sais pas ce que fait votre getCookie()méthode, mais dans mon cas, j'ai dû appeler.

cookieManager.removeSessionCookie();

Commencez par supprimer le cookie de session, puis ajoutez-le à nouveau. J'ai découvert que lorsque j'ai essayé de définir le JSESSIONIDcookie sans le supprimer au préalable, la valeur que je voulais définir n'était pas enregistrée. Je ne sais pas si cela vous aidera à résoudre un problème particulier, mais j'ai pensé que je partagerais ce que j'avais trouvé.

Justin
la source
pourquoi pas CookieManager.getInstance().removeAllCookie ();?
Francisco Corrales Morales
2

Après quelques recherches, j'ai rassemblé quelques éléments qui m'ont permis d'arriver à cette solution. Une fois que CookieSyncManager est obsolète, cela peut être le meilleur moyen de définir un cookie spécifique pour une vue Web dans Kotlin de nos jours, vous ne devriez plus avoir besoin de rien d'autre.

private fun setCookie(){
    val webView = WebView(this) // this = context
    val cookieManager = CookieManager.getInstance()
    cookieManager.acceptCookie()

    val domain = "https://www.yourdomain.com/"

    webView.webViewClient = WebViewClient()
    webView.settings.javaScriptEnabled = true

    cookieManager.setCookie(domain,"$cookieKey=$cookieValue")
    cookieManager.setAcceptThirdPartyCookies(webView, true)

    webView.loadUrl(domain)
}
Samuel Luís
la source
1

J'ai une approche différente des autres personnes ici, et c'est une approche qui est garantie de fonctionner sans avoir à traiter avec le CookieSyncManager (où vous êtes à la merci d'une sémantique comme "Notez que même sync () se produit de manière asynchrone").

Essentiellement, nous naviguons vers le bon domaine, puis nous exécutons javascript à partir du contexte de la page pour définir des cookies pour ce domaine (de la même manière que la page elle-même). Deux inconvénients de la méthode sont qu'ils peuvent introduire un temps d'aller-retour supplémentaire en raison de la requête http supplémentaire que vous devez effectuer; et si votre site n'a pas l'équivalent d'une page vierge, il peut flasher quelle que soit l'URL que vous chargez en premier avant de vous emmener au bon endroit.

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.http.cookie.Cookie;
import android.annotation.SuppressLint;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class WebViewFragment {
    private static final String BLANK_PAGE = "/blank.html"

    private CookieSyncManager mSyncManager;
    private CookieManager mCookieManager;

    private String mTargetUrl;
    private boolean mInitializedCookies;
    private List<Cookie> mAllCookies;

    public WebViewFragment(Context ctx) {
        // We are still required to create an instance of Cookie/SyncManager.
        mSyncManager = CookieSyncManager.createInstance(ctx);
        mCookieManager = CookieManager.getInstance();
    }

    @SuppressLint("SetJavaScriptEnabled") public void loadWebView(
                String url, List<Cookie> cookies, String domain) {
        final WebView webView = ...

        webView.setWebViewClient(new CookeWebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);

        mInitializedCookies = false;
        mTargetUrl = url;
        mAllCookies = cookies;
        // This is where the hack starts.
        // Instead of loading the url, we load a blank page.
        webView.loadUrl("http://" + domain + BLANK_PAGE);
    }

    public static String buildCookieString(final Cookie cookie) {
        // You may want to add the secure flag for https:
        // + "; secure"
        // In case you wish to convert session cookies to have an expiration:
        // + "; expires=Thu, 01-Jan-2037 00:00:10 GMT"
        // Note that you cannot set the HttpOnly flag as we are using
        // javascript to set the cookies.
        return cookie.getName() + "=" + cookie.getValue()
                    + "; path=" + cookie.getPath()
                    + "; domain=" + cookie.getDomain()
    };

    public synchronized String generateCookieJavascript() {
        StringBuilder javascriptCode = new StringBuilder();
        javascriptCode.append("javascript:(function(){");
        for (final Cookie cookie : mAllCookies) {
            String cookieString = buildCookieString(cookie);
            javascriptCode.append("document.cookie=\"");
            javascriptCode.append(
                     StringEscapeUtils.escapeJavascriptString(cookieString));
            javascriptCode.append("\";");
        }
        // We use javascript to load the next url because we do not
        // receive an onPageFinished event when this code finishes.
        javascriptCode.append("document.location=\"");
        javascriptCode.append(
                StringEscapeUtils.escapeJavascriptString(mTargetUrl));
        javascriptCode.append("\";})();");
        return javascriptCode.toString();
    }

    private class CookieWebViewClient extends WebViewClient {
        @Override public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (!mInitializedCookies) {
                mInitializedCookies = true;
                // Run our javascript code now that the temp page is loaded.
                view.loadUrl(generateCookieJavascript());
                return;
            }
        }
    }
}

Si vous faites confiance au domaine d'où proviennent les cookies, vous pourrez peut-être vous en sortir sans apache commons, mais vous devez comprendre que cela peut présenter un risque XSS si vous ne faites pas attention.

Patrick Horn
la source
1

C'est un morceau de code fonctionnel.

    private void setCookie(DefaultHttpClient httpClient, String url) {
    List<Cookie> cookies = httpClient.getCookieStore().getCookies();
    if (cookies != null) {
        CookieSyncManager.createInstance(context);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);

        for (int i = 0; i < cookies.size(); i++) {
            Cookie cookie = cookies.get(i);
            String cookieString = cookie.getName() + "=" + cookie.getValue();
            cookieManager.setCookie(url, cookieString);
        }
        CookieSyncManager.getInstance().sync();
    }
}

Ici, le httpclient est l'objet DefaultHttpClient que vous avez utilisé dans la requête HttpGet / HttpPost. Aussi une chose à vérifier est le nom et la valeur du cookie, il doit être donné

String cookieString = cookie.getName() + "=" + cookie.getValue();

setCookie définira le cookie pour l'URL donnée.

enfant droïde
la source
Juste une note: vous ne pouvez pas stocker les cookies de session en utilisant CookieManager.setCookie
John Doe
je n'ai pas compris ... pouvez-vous expliquer?
droid kid
1

J'ai résolu par magie tous mes problèmes de cookies avec cette seule ligne dans onCreate:

CookieHandler.setDefault(new CookieManager());

edit: il a cessé de fonctionner aujourd'hui. :( quelle merde, android.

Chani
la source
alors, des mises à jour? pourquoi a-t-il cessé de fonctionner?, l'avez-vous résolu?
Francisco Corrales Morales
1

J'ai rencontré cela aussi. Voici ce que j'ai fait.

Sur mon LoginActivity, dans mon AsyncTask, j'ai ce qui suit:

CookieStoreHelper.cookieStore = new BasicCookieStore();
BasicHttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, CookieStoreHelper.cookieStore);

HttpResponse postResponse = client.execute(httpPost,localContext);
CookieStoreHelper.sessionCookie = CookieStoreHelper.cookieStore.getCookies();

// WHERE CookieStoreHelper.sessionCookie est une autre classe contenant la variable sessionCookie définie comme List cookies; et cookieStore défini comme BasicCookieStore cookieStore;

Ensuite, sur mon fragment, où se trouve mon WebView, j'ai ce qui suit:

//DECLARE LIST OF COOKIE
List<Cookie> sessionCookie;

dans ma méthode ou juste avant de définir le WebViewClient ()

WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);

sessionCookie = CookieStoreHelper.cookieStore.getCookies();
CookieSyncManager.createInstance(webView.getContext());
CookieSyncManager.getInstance().startSync();
CookieManager cookieManager = CookieManager.getInstance();
CookieManager.getInstance().setAcceptCookie(true);
if (sessionCookie != null) {
   for(Cookie c:  sessionCookie){
      cookieManager.setCookie(CookieStoreHelper.DOMAIN, c.getName() + "=" + c.getValue());
   }
   CookieSyncManager.getInstance().sync();

 }

 webView.setWebViewClient(new WebViewClient() {
    //AND SO ON, YOUR CODE
 }

Astuce rapide: installez Firebug sur Firefox ou utilisez la console de développement sur Chrome et testez d'abord votre page Web, capturez le cookie et vérifiez le domaine afin de pouvoir le stocker quelque part et être sûr que vous définissez correctement le bon domaine.

Edit: modification de CookieStoreHelper.cookies en CookieStoreHelper.sessionCookie

Burnok
la source
1

Mon code de travail

public View onCreateView(...){
    mWebView = (WebView) view.findViewById(R.id.webview);

    WebSettings webSettings = mWebView.getSettings();
    webSettings.setJavaScriptEnabled(true);

        ...
        ...
        ...

    CookieSyncManager.createInstance(mWebView.getContext());
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.setAcceptCookie(true);
    //cookieManager.removeSessionCookie(); // remove
    cookieManager.removeAllCookie(); //remove
    // Recommended "hack" with a delay between the removal and the installation of "Cookies"
    SystemClock.sleep(1000);

    cookieManager.setCookie("https://my.app.site.com/", "cookiename=" + value + "; path=/registration" + "; secure"); // ;
    CookieSyncManager.getInstance().sync();

    mWebView.loadUrl(sp.getString("url", "") + end_url);

    return view;
}

Pour déboguer la requête, "cookieManager.setCookie (....);" Je vous recommande de parcourir le contenu de la base de données webviewCookiesChromium.db (stockée dans "/data/data/my.app.webview/database") Là, vous pouvez voir les paramètres corrects.

Désactivation de "cookieManager.removeSessionCookie ();" et / ou "cookieManager.removeAllCookie ();"

//cookieManager.removeSessionCookie();
// and/or
//cookieManager.removeAllCookie();"

Comparez la valeur définie avec celles définies par le navigateur. Ajustez la demande d'installation des cookies avant jusqu'à ce que le navigateur "flags" ne soit pas installé correspondra à ce que vous décidez. J'ai trouvé qu'une requête peut être des "drapeaux":

// You may want to add the secure flag for https:
+ "; secure"
// In case you wish to convert session cookies to have an expiration:
+ "; expires=Thu, 01-Jan-2037 00:00:10 GMT"
// These flags I found in the database:
+ "; path=/registration"
+ "; domain=my.app.site.com"
Vadim.Ivanov
la source
J'ai utilisé le code ci-dessus pour stocker le cookie dans la vue Web, mais s'il vous plaît laissez-moi savoir sur le chemin. quel est le chemin ici?
Mehul Tank
@MehulTank Le paramètre path spécifie un emplacement de document. Le cookie n'est envoyé au serveur que si le chemin correspond à l'emplacement actuel ou à un emplacement du document parent.
Roland van der Linden
1

Quelques commentaires (au moins pour les API> = 21) que j'ai découverts de mon expérience et qui m'ont donné des maux de tête:

  1. httpet les httpsURL sont différentes. Définir un cookie pour http://www.example.comest différent de définir un cookie pourhttps://www.example.com
  2. Une barre oblique à la fin de l'URL peut également faire la différence. Dans mon cas https://www.example.com/fonctionne mais https://www.example.comne fonctionne pas.
  3. CookieManager.getInstance().setCookieexécute une opération asynchrone. Ainsi, si vous chargez une URL immédiatement après l'avoir définie, il n'est pas garanti que les cookies auront déjà été écrits. Pour éviter les comportements inattendus et instables, utilisez le CookieManager # setCookie (String url, String value, ValueCallback callback) ( lien ) et commencez à charger l'url après l'appel du rappel.

J'espère que mes deux centimes feront gagner du temps à certaines personnes pour que vous n'ayez pas à faire face aux mêmes problèmes que moi.

giorgos.nl
la source
comment différent est un cookie pour example.com est différent de un cookie pour example.com ?
uzu
0

J'ai rencontré le même problème et cela résoudra ce problème dans toutes les versions d'Android

private void setCookie() {
    try {
        CookieSyncManager.createInstance(context);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            cookieManager.setCookie(Constant.BASE_URL, getCookie(), value -> {
                String cookie = cookieManager.getCookie(Constant.BASE_URL);
                CookieManager.getInstance().flush();
                CustomLog.d("cookie", "cookie ------>" + cookie);
                setupWebView();
            });
        } else {
            cookieManager.setCookie(webUrl, getCookie());
            new Handler().postDelayed(this::setupWebView, 700);
            CookieSyncManager.getInstance().sync();
        }

    } catch (Exception e) {
        CustomLog.e(e);
    }
}
Prinkal Kumar
la source
0

Notez qu'il peut être préférable d'utiliser des sous-domaines au lieu de l'URL habituelle. Alors, définissez .example.comau lieu de https://example.com/.

Merci à Jody Jacobus Geers et à d'autres, j'ai écrit ainsi:

if (savedInstanceState == null) {
    val cookieManager = CookieManager.getInstance()
    cookieManager.acceptCookie()
    val domain = ".example.com"
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        cookieManager.setCookie(domain, "token=$token") {
            view.webView.loadUrl(url)
        }
        cookieManager.setAcceptThirdPartyCookies(view.webView, true)
    } else {
        cookieManager.setCookie(domain, "token=$token")
        view.webView.loadUrl(url)
    }
} else {
    // Check whether we're recreating a previously destroyed instance.
    view.webView.restoreState(savedInstanceState)
}
CoolMind
la source
-4

N'utilisez pas votre URL brute

Au lieu de:

cookieManager.setCookie(myUrl, cookieString); 

utilisez-le comme ceci:

cookieManager.setCookie("your url host", cookieString); 
Jason
la source