Problema con i cookie di Android WebView


89

Ho un server che invia alla mia app Android un cookie di sessione da utilizzare per la comunicazione autenticata. Sto cercando di caricare un WebView con un URL che punta a quello stesso server e sto cercando di passare il cookie di sessione per l'autenticazione. Sto osservando che funziona a intermittenza ma non ho idea del perché. Uso lo stesso cookie di sessione per effettuare altre chiamate sul mio server e queste non falliscono mai l'autenticazione. Osservo questo problema solo quando provo a caricare un URL in un WebView e non si verifica ogni volta. Molto frustrante.

Di seguito è riportato il codice che sto usando per farlo. Qualsiasi aiuto sarà molto apprezzato.

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

fare riferimento a questa domanda stackoverflow.com/questions/2566485/...
neeraj t

Risposte:


55

Grazie justingrammens ! Per me ha funzionato, sono riuscito a condividere il cookie all'interno delle mie richieste DefaultHttpClient e dell'attività 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();
}   

Questo ha funzionato benissimo per me. Ho costruito il mio URL del cookie in questo modo: String url = (cookie.isSecure ()? "Https": "http") + ": //" + cookie.getDomain () + cookie.getPath ();
Heath Borders

grazie per il post ... mi ha aiutato a implementare il logout di Twitter per la mia app ...;)
rahul

puoi dire cosa scrivere al posto di myapp.cookie
suraj jain

la parola chiave è: String cookieString = sessionCookie.getName () + "=" + sessionCookie.getValue () + "; domain =" + sessionCookie.getDomain (); cookieManager.setCookie (myapp.domain, cookieString);
Zennichimaro

1
CookieSyncManager è ora deprecato :(
Misha Akopov

18

Grazie Android per aver rovinato la mia domenica. . . Ecco cosa ha risolto le mie app (dopo aver avviato la visualizzazione web)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

Dovrei dire che le risposte di cui sopra probabilmente funzioneranno, ma nella mia situazione nel momento in cui Android è diventato v5 + le mie "app" javascript di Android Webview sono morte.


1
Oh! Mi hai appena salvato la giornata!
Le Minh

14

Soluzione: 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");

da cosa ottieni cookie?, ottengo solo come: PHPSESSID=ljfakdjfklasdfaj!, è abbastanza?
Francisco Corrales Morales

6
Cos'è MySession qui?
Utente3


3

Salverei quel cookie di sessione come preferenza e con esso ripopolerei forzatamente il gestore dei cookie. Sembra che il cookie di sessione non sopravviva al riavvio dell'attività


Devo aggiungere che la mia app effettua molte altre chiamate non WebView sul mio server che non falliscono mai l'autenticazione. Solo quando provo a caricare un URL in un WebView noto questo problema. "Cookie sessionCookie = getCookie ();" sta recuperando dal DB dell'app il cookie di sessione che utilizzo per tutti i messaggi con il mio server.
nannerpus

Bene, se stai usando HttpClient per questo ha il suo cookie store, quindi se tieni la singola istanza del client il tuo cookie sopravviverà, ma potrebbe non avere nulla a che fare con quello usato dalla tua visualizzazione web
Bostone

Se ho capito bene, stai dicendo che CookieManager restituito da CookieManager.getInstance (); influenzerà i cookie utilizzati dalle istanze di HttpClient, che WebViews non utilizza. Se questo è vero, sai come posso trasferire esplicitamente i cookie in WebViews? Inoltre, guardando la documentazione di CookieManager, forse non chiamare "cookieManager.setAcceptCookie (true)" mi sta causando problemi? Grazie per l'aiuto, lo apprezzo davvero.
nannerpus

3

Ho trascorso la maggior parte delle tre ore a lavorare su un problema molto simile. Nel mio caso ho avuto un numero di chiamate, che ho effettuato a un servizio web utilizzando un DefaulHttpCliente quindi ho voluto impostare la sessione e tutti gli altri cookie corrispondenti nel mio file WebView.

Non so se questo risolverà il tuo problema, dal momento che non so cosa fa il tuo getCookie()metodo, ma nel mio caso ho dovuto chiamare.

cookieManager.removeSessionCookie();

Per prima cosa rimuovere il cookie di sessione e quindi aggiungerlo di nuovo. Stavo scoprendo che quando ho provato a impostare il JSESSIONIDcookie senza prima rimuoverlo, il valore su cui volevo impostarlo non veniva salvato. Non sono sicuro se questo ti aiuterà in particolare problema, ma ho pensato di condividere ciò che avevo trovato.


perchè no CookieManager.getInstance().removeAllCookie ();?
Francisco Corrales Morales

2

Dopo un po 'di ricerca ho raccolto alcuni pezzi che mi hanno fatto arrivare a questa soluzione. Una volta che CookieSyncManager è deprecato, questo potrebbe essere il modo migliore per impostare un cookie specifico per una visualizzazione web in Kotlin al giorno d'oggi, non dovresti aver bisogno di nient'altro.

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

1

Ho un approccio diverso da altre persone qui, ed è un approccio che è garantito il lavoro senza trattare con CookieSyncManager (dove sei in balia di semantiche come "Nota che anche sync () avviene in modo asincrono").

In sostanza, navighiamo al dominio corretto, quindi eseguiamo javascript dal contesto della pagina per impostare i cookie per quel dominio (allo stesso modo della pagina stessa). Due svantaggi del metodo sono che possono introdurre un tempo di andata e ritorno aggiuntivo a causa della richiesta http aggiuntiva che devi fare; e se il tuo sito non ha l'equivalente di una pagina vuota, potrebbe lampeggiare qualsiasi URL caricato per primo prima di portarti nel posto giusto.

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

Se ti fidi del dominio da cui provengono i cookie, potresti riuscire a cavartela senza Apache Common, ma devi capire che questo può presentare un rischio XSS se non stai attento.


1

Questo è un pezzo di codice funzionante.

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

Qui httpclient è l'oggetto DefaultHttpClient che hai usato nella richiesta HttpGet / HttpPost. Inoltre una cosa da assicurarsi è il nome e il valore del cookie, dovrebbe essere dato

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

setCookie imposterà il cookie per l'URL specificato.


Come nota: non è possibile memorizzare i cookie di sessione utilizzando CookieManager.setCookie
John Doe

non ho capito ... puoi spiegarmi?
ragazzo droide

1

Ho magicamente risolto tutti i miei problemi con i cookie con questa riga in onCreate:

CookieHandler.setDefault(new CookieManager());

modifica: ha smesso di funzionare oggi. :( che schifo, Android.


quindi, eventuali aggiornamenti? perché ha smesso di funzionare? l'hai risolto?
Francisco Corrales Morales

1

Incontrato anche questo. Ecco cosa ho fatto.

Sul mio LoginActivity, all'interno del mio AsyncTask, ho quanto segue:

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 è un'altra classe contenente la variabile sessionCookie definita come List cookies; e cookieStore definiscono come BasicCookieStore cookieStore;

Quindi sul mio frammento, dove si trova il mio WebView, ho quanto segue:

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

all'interno del mio metodo o appena prima di impostare 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
 }

Suggerimento rapido: installa firebug su Firefox o utilizza la console per sviluppatori su Chrome e testa prima la tua pagina web, acquisisci il cookie e controlla il dominio in modo da poterlo memorizzare da qualche parte e assicurarti di aver impostato correttamente il dominio giusto.

Modifica: cookieStoreHelper modificati su CookieStoreHelper.sessionCookie


1

Il mio codice di lavoro

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

Per eseguire il debug della query, "cookieManager.setCookie (....);" Vi consiglio di consultare il contenuto del database webviewCookiesChromium.db (memorizzato in "/data/data/my.app.webview/database") Lì potete vedere le impostazioni corrette.

Disattivazione di "cookieManager.removeSessionCookie ();" e / o "cookieManager.removeAllCookie ();"

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

Confronta il valore impostato con quelli impostati dal browser. Regola la richiesta di installazione dei cookie prima che il browser "flag" non sia installato si adatterà a ciò che decidi. Ho scoperto che una query può essere "flag":

// 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"

Ho usato il codice sopra per memorizzare i cookie nella visualizzazione web ma per favore fatemi sapere del percorso. qual è il percorso qui?
Mehul Tank il

@MehulTank Il parametro path specifica una posizione del documento. Il cookie viene inviato al server solo se il percorso corrisponde alla posizione del documento corrente o principale.
Roland van der Linden

1

Un paio di commenti (almeno per le API> = 21) che ho scoperto dalla mia esperienza e che mi hanno fatto venire il mal di testa:

  1. httpe gli httpsURL sono diversi. Impostazione di un cookie perhttp://www.example.com è diversa dall'impostazione di un cookie perhttps://www.example.com
  2. Anche una barra alla fine dell'URL può fare la differenza. Nel mio caso https://www.example.com/funziona mahttps://www.example.com non funziona.
  3. CookieManager.getInstance().setCookiesta eseguendo un'operazione asincrona. Quindi, se carichi un URL subito dopo averlo impostato, non è garantito che i cookie siano già stati scritti. Per prevenire comportamenti imprevisti e instabili, utilizzare CookieManager # setCookie (String url, String value, ValueCallback callback) ( link ) e iniziare a caricare l'URL dopo che verrà chiamato il callback.

Spero che i miei due centesimi facciano risparmiare tempo ad alcune persone, così non dovrai affrontare gli stessi problemi come ho fatto io.


quanto è diverso l'impostazione di un cookie per example.com rispetto all'impostazione di un cookie per example.com ?
softmarshmallow

0

Ho affrontato lo stesso problema e risolverà questo problema in tutte le versioni di 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);
    }
}

0

Nota che potrebbe essere meglio usare sottodomini invece del solito URL. Quindi, imposta .example.cominvece di https://example.com/.

Grazie a Jody Jacobus Geers e altri ho scritto così:

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

-4

Non utilizzare il tuo URL grezzo

Invece di:

cookieManager.setCookie(myUrl, cookieString); 

usalo in questo modo:

cookieManager.setCookie("your url host", cookieString); 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.