Problem z plikami cookie w Android WebView

89

Mam serwer, który wysyła do mojej aplikacji na Androida plik cookie sesji, który ma być używany do uwierzytelnionej komunikacji. Próbuję załadować WebView z adresem URL wskazującym na ten sam serwer i próbuję przekazać plik cookie sesji do uwierzytelnienia. Obserwuję, że działa sporadycznie, ale nie mam pojęcia dlaczego. Używam tego samego pliku cookie sesji, aby wykonywać inne wywołania na moim serwerze, a uwierzytelnianie nigdy nie kończy się niepowodzeniem. Obserwuję ten problem tylko podczas próby załadowania adresu URL w WebView i nie zdarza się to za każdym razem. Bardzo frustrujące.

Poniżej znajduje się kod, którego używam do tego. Każda pomoc będzie bardzo mile widziana.

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
źródło
skieruj

Odpowiedzi:

55

Dzięki justingrammens ! To zadziałało, udało mi się udostępnić plik cookie w ramach moich żądań DefaultHttpClient i aktywności 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
źródło
To działało świetnie dla mnie. W ten sposób utworzyłem swój adres URL pliku cookie: String url = (cookie.isSecure ()? "Https": "http") + ": //" + cookie.getDomain () + cookie.getPath ();
Heath Borders
dzięki za post ... pomogło mi to we wdrożeniu wylogowania z Twittera dla mojej aplikacji ...;)
rahul
czy możesz powiedzieć, co ma zostać zapisane zamiast myapp.cookie
suraj jain
słowo kluczowe to: String cookieString = sessionCookie.getName () + "=" + sessionCookie.getValue () + "; domain =" + sessionCookie.getDomain (); cookieManager.setCookie (myapp.domain, cookieString);
Zennichimaro
1
CookieSyncManager jest teraz przestarzały :(
Misha Akopov
18

Dzięki Androidowi za zrujnowanie mojej niedzieli. . . Oto, co naprawiło moje aplikacje (po uruchomieniu widoku internetowego)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

Powinienem powiedzieć, że powyższe odpowiedzi prawdopodobnie zadziałają, ale w mojej sytuacji w momencie, gdy Android przeszedł na wersję 5 +, moje `` aplikacje '' javascript webview na Androida umarły.

Jody Jacobus Geers
źródło
1
Och! Właśnie uratowałeś mi dzień!
Le Minh
14

Rozwiązanie: 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
źródło
co otrzymujesz cookie?, dostaję tylko: PHPSESSID=ljfakdjfklasdfaj!, czy to wystarczy?
Francisco Corrales Morales
6
Czym jest tutaj MySession?
User3
3

Zapisałbym ten sesyjny plik cookie jako preferencję i na siłę ponownie zapełniłbym nim menedżera plików cookie. Wygląda na to, że plik cookie sesji nie przetrwał ponownego uruchomienia działania

Bostone
źródło
Powinienem dodać, że moja aplikacja wykonuje wiele innych wywołań innych niż WebView na moim serwerze, które nigdy nie kończą się niepowodzeniem uwierzytelniania. Dopiero gdy próbuję załadować adres URL w WebView, zauważam ten problem. "Cookie sessionCookie = getCookie ();" pobiera z bazy danych aplikacji plik cookie sesji, którego używam dla wszystkich wiadomości na moim serwerze.
nannerpus
Cóż, jeśli używasz HttpClient do tego, że ma on swój własny magazyn plików cookie, więc jeśli trzymasz jedną instancję klienta, Twój plik cookie przetrwa, ale może nie mieć nic wspólnego z tym używanym przez twoją stronę internetową
Bostone
Jeśli dobrze rozumiem, twierdzisz, że plik CookieManager zwrócony przez CookieManager.getInstance (); wpłynie na pliki cookie używane przez wystąpienia HttpClient, których WebViews nie używa. Jeśli to prawda, czy wiesz, w jaki sposób mogę jawnie przekazywać pliki cookie do WebViews? Patrząc na dokumenty CookieManager, być może brak wywołania „cookieManager.setAcceptCookie (true)” powoduje problemy? Dzięki za pomoc, naprawdę to doceniam.
nannerpus
3

Większą połowę 3 godzin spędziłem nad bardzo podobnym problemem. W moim przypadku miałem kilka połączeń, które wykonałem z usługą internetową za pomocą a, DefaulHttpClienta następnie chciałem ustawić sesję i wszystkie inne odpowiednie pliki cookie w moim WebView.

Nie wiem, czy to rozwiąże twój problem, ponieważ nie wiem, co getCookie()robi twoja metoda, ale w moim przypadku faktycznie musiałem zadzwonić.

cookieManager.removeSessionCookie();

Najpierw usuń plik cookie sesji, a następnie dodaj go ponownie. Odkryłem, że kiedy próbowałem ustawić JSESSIONIDplik cookie bez uprzedniego usunięcia go, wartość, na którą chciałem go ustawić, nie była zapisywana. Nie jestem pewien, czy to pomoże ci w konkretnym problemie, ale pomyślałem, że podzielę się tym, co znalazłem.

Justin
źródło
dlaczego nie CookieManager.getInstance().removeAllCookie ();?
Francisco Corrales Morales
2

Po pewnym czasie poszukiwań zebrałem kilka elementów, które doprowadziły mnie do tego rozwiązania. Gdy CookieSyncManager zostanie przestarzały, może to być obecnie najlepszy sposób na ustawienie określonego pliku cookie dla widoku internetowego w Kotlin, nie powinieneś potrzebować niczego więcej.

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
źródło
1

Mam inne podejście niż inni ludzie tutaj i jest to podejście, które gwarantuje pracę bez zajmowania się CookieSyncManager (gdzie jesteś na łasce semantyki, takiej jak „Zauważ, że nawet sync () odbywa się asynchronicznie”).

Zasadniczo przechodzimy do właściwej domeny, a następnie wykonujemy javascript z kontekstu strony, aby ustawić pliki cookie dla tej domeny (tak samo jak sama strona). Dwie wady tej metody to to, że może to spowodować wydłużenie czasu podróży w obie strony ze względu na dodatkowe żądanie http, które musisz wykonać; a jeśli Twoja witryna nie ma odpowiednika pustej strony, może sflashować dowolny adres URL, który załadujesz jako pierwszy, zanim przeniesie Cię we właściwe miejsce.

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

Jeśli ufasz domenie, z której pochodzą pliki cookie, możesz uciec bez apache commons, ale musisz zrozumieć, że może to stanowić ryzyko XSS, jeśli nie będziesz ostrożny.

Patrick Horn
źródło
1

To jest działający fragment kodu.

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

Tutaj httpclient jest obiektem DefaultHttpClient, który został użyty w żądaniu HttpGet / HttpPost. Należy również upewnić się, że należy podać nazwę i wartość pliku cookie

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

setCookie ustawi plik cookie dla podanego adresu URL.

droid dzieciak
źródło
Uwaga: nie można przechowywać plików cookie sesji za pomocą CookieManager.setCookie
John Doe
nie rozumiem ... czy możesz wyjaśnić?
droid dzieciak
1

W magiczny sposób rozwiązałem wszystkie swoje problemy z plikami cookie za pomocą tej jednej linii w onCreate:

CookieHandler.setDefault(new CookieManager());

edycja: przestała działać dzisiaj. :( co do cholery, android.

Chani
źródło
więc jakieś aktualizacje? dlaczego to przestało działać?, czy rozwiązałeś to?
Francisco Corrales Morales
1

To też spotkałem. Oto co zrobiłem.

Na moim LoginActivity, wewnątrz mojego AsyncTask, mam następujące elementy:

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 to kolejna klasa zawierająca zmienną sessionCookie zdefiniowaną jako Lista plików cookie; a cookieStore definiuje się jako BasicCookieStore cookieStore;

Następnie na moim fragmencie, w którym znajduje się mój WebView, mam następujące elementy:

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

wewnątrz mojej metody lub tuż przed ustawieniem 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
 }

Szybka wskazówka: zainstaluj program firebug w przeglądarce Firefox lub użyj konsoli programisty w przeglądarce Chrome i najpierw przetestuj swoją stronę internetową, przechwyć plik cookie i sprawdź domenę, aby móc ją gdzieś przechowywać i upewnić się, że prawidłowo ustawiasz właściwą domenę.

Edycja: edytowano CookieStoreHelper.cookies do CookieStoreHelper.sessionCookie

Burnok
źródło
1

Mój działający kod

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

Aby debugować zapytanie, „cookieManager.setCookie (....);” Polecam przejrzenie zawartości bazy danych webviewCookiesChromium.db (przechowywanej w "/data/data/my.app.webview/database"). Tam możesz zobaczyć prawidłowe ustawienia.

Wyłączenie "cookieManager.removeSessionCookie ();" i / lub „cookieManager.removeAllCookie ();”

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

Porównaj ustawione wartości z wartościami ustawionymi przez przeglądarkę. Dostosuj żądanie instalacji plików cookie, zanim "flagi" nie zostaną zainstalowane, przeglądarka będzie pasować do tego, co zdecydujesz. Zauważyłem, że zapytanie może być „flagami”:

// 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
źródło
Użyłem powyższego kodu do przechowywania plików cookie w widoku sieciowym, ale proszę o informację o ścieżce. jaka jest ścieżka?
Mehul Tank
@MehulTank Parametr ścieżki określa lokalizację dokumentu. Plik cookie jest wysyłany do serwera tylko wtedy, gdy ścieżka odpowiada bieżącej lub nadrzędnej lokalizacji dokumentu.
Roland van der Linden
1

Kilka komentarzy (przynajmniej dla API> = 21), których dowiedziałem się z mojego doświadczenia i przyprawiło mnie o ból głowy:

  1. httpi httpsadresy URL są różne. Ustawienie pliku cookie dla http://www.example.comróżni się od ustawienia pliku cookie dlahttps://www.example.com
  2. Ukośnik na końcu adresu URL również może mieć znaczenie. W moim przypadku https://www.example.com/działa, ale https://www.example.comnie działa.
  3. CookieManager.getInstance().setCookiewykonuje operację asynchroniczną. Jeśli więc załadujesz adres URL zaraz po jego ustawieniu, nie ma gwarancji, że pliki cookie zostaną już zapisane. Aby zapobiec nieoczekiwanym i niestabilnym zachowaniom, użyj CookieManager # setCookie (String url, String value, ValueCallback callback) ( link ) i rozpocznij ładowanie adresu URL po wywołaniu wywołania zwrotnego.

Mam nadzieję, że moje dwa grosze zaoszczędzą trochę czasu niektórym ludziom, więc nie będziesz musiał stawić czoła tym samym problemom, co ja.

giorgos.nl
źródło
Czym różni się ustawienie pliku cookie dla example.com od ustawienia pliku cookie dla example.com ?
softmarshmallow
0

Napotkałem ten sam problem i rozwiąże ten problem we wszystkich wersjach Androida

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
źródło
0

Zauważ, że może lepiej jest użyć subdomen zamiast zwykłego adresu URL. Więc .example.comzamiast https://example.com/.

Dzięki Jody Jacobus Geers i innym napisałem tak:

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
źródło
-4

Nie używaj surowego adresu URL

Zamiast:

cookieManager.setCookie(myUrl, cookieString); 

użyj tego w ten sposób:

cookieManager.setCookie("your url host", cookieString); 
Jason
źródło