Проблема файлу cookie Android WebView


89

У мене є сервер, який надсилає моєму додатку для Android сеансовий файл cookie для використання для автентифікованого зв'язку. Я намагаюся завантажити WebView з URL-адресою, що вказує на той самий сервер, і намагаюся передати сеансовий файл cookie для автентифікації. Я спостерігаю, що це працює з перебоями, але я не уявляю, чому. Я використовую той самий сеансовий файл cookie для здійснення інших дзвінків на моєму сервері, і вони ніколи не провалюють аутентифікацію. Я спостерігаю цю проблему лише при спробі завантажити URL-адресу у WebView, і це трапляється не кожен раз. Дуже розчаровує.

Нижче наведено код, який я використовую для цього. Будь-яка допомога буде вдячна.

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

передати це питання stackoverflow.com/questions/2566485 / ...
Neeraj т

Відповіді:


55

Дякую justingrammens ! Це спрацювало для мене, мені вдалося поділитися файлом cookie в межах моїх запитів DefaultHttpClient та діяльності 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();
}   

Для мене це чудово працювало. Я створив свою URL-адресу cookie таким чином: String url = (cookie.isSecure ()? "Https": "http") + ": //" + cookie.getDomain () + cookie.getPath ();
Heath Borders

спасибі за допис ... це допомогло мені в реалізації виходу з Twitter для мого додатка ...;)
rahul

можеш сказати, що слід писати замість myapp.cookie
suraj jain

ключове слово: String cookieString = sessionCookie.getName () + "=" + sessionCookie.getValue () + "; domain =" + sessionCookie.getDomain (); cookieManager.setCookie (myapp.domain, cookieString);
Zennichimaro

1
CookieSyncManager зараз застарів :(
Міша Акопов,

18

Дякую Android, що зіпсував мою неділю. . . Ось що виправили мої програми (після того, як ви ініціюєте свій веб-перегляд)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

Слід сказати, що наведені вище відповіді, мабуть, спрацюють, але в моїй ситуації, як тільки Android пішов v5 +, мої андроїд-веб-перегляди JavaScript-додатків загинули.


1
О! Ви щойно врятували мій день!
Ле Мін

14

Рішення: 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");

що ти отримуєш від cookie?, я отримую лише таке: PHPSESSID=ljfakdjfklasdfaj!, цього достатньо?
Франсіско Корралес Моралес

6
Що тут MySession?
Користувач3


3

Я хотів би зберегти цей сеансовий файл cookie як преференцію і з силою повторно заповнити ним менеджер файлів cookie. Здається, файли cookie сеансу не пережили перезавантаження активності


Слід додати, що мій додаток робить багато інших не-WebView на дзвінках мого сервера, які ніколи не провалюють автентифікацію. Лише коли я намагаюся завантажити URL-адресу у WebView, я помічаю цю проблему. "Файл cookie sessionCookie = getCookie ();" отримує з БД програми сеансовий файл cookie, який я використовую для всіх повідомлень на своєму сервері.
nannerpus

Добре, якщо ви використовуєте HttpClient для цього, він має власний магазин cookie, тому, якщо ви тримаєте один екземпляр клієнта, ваш файл cookie виживе, але він може не мати нічого спільного з тим, що використовується вашим веб-переглядом
Bostone

Якщо я вас правильно розумію, ви говорите, що CookieManager повернуто CookieManager.getInstance (); вплине на файли cookie, що використовуються екземплярами HttpClient, які WebViews не використовує. Якщо це правда, чи знаєте ви, як я можу явно передавати файли cookie у WebViews? Крім того, дивлячись на документи CookieManager, можливо, не викликаючи "cookieManager.setAcceptCookie (true)" викликає у мене проблеми? Дякую за допомогу, справді вдячний.
nannerpus

3

Я провів більшу половину 3 годин, працюючи над дуже подібним питанням. У моєму випадку у мене було кілька дзвінків, які я зробив до веб-служби за допомогою a, DefaulHttpClientа потім хотів встановити сеанс та всі інші відповідні файли cookie в моєму WebView.

Я не знаю, чи це вирішить вашу проблему, оскільки я не знаю, що getCookie()робить ваш метод, але в моєму випадку мені насправді довелося зателефонувати.

cookieManager.removeSessionCookie();

Спочатку видаліть файли cookie сеансу, а потім знову додайте його. Я виявив, що коли я намагався встановити JSESSIONIDфайл cookie, не попередньо видаляючи його, значення, яке я хотів встановити, не зберігалося. Не впевнений, чи допоможе це вам у конкретній проблемі, але думав, що поділюся тим, що знайшов.


чому ні CookieManager.getInstance().removeAllCookie ();?
Франсіско Корралес Моралес

2

Через деякий час дослідження я зібрав кілька частин, які змусили мене дійти до цього рішення. Після того, як CookieSyncManager застаріло, це може бути найкращим способом встановити певний файл cookie для веб-перегляду в Kotlin сьогодні, вам більше нічого не потрібно.

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

У мене інший підхід, ніж у інших людей тут, і це підхід, який гарантовано спрацьовує без роботи з CookieSyncManager (де ви перебуваєте у владі семантики на кшталт "Зверніть увагу, що навіть синхронізація () відбувається асинхронно").

По суті, ми переглядаємо правильний домен, потім виконуємо javascript з контексту сторінки, щоб встановити файли cookie для цього домену (так само, як це робила б сама сторінка). Два недоліки методу полягають у тому, що вони можуть ввести додатковий час туди-назад через додатковий http-запит, який ви повинні зробити; а якщо на вашому веб-сайті немає еквівалента порожньої сторінки, він може спалахувати будь-яку URL-адресу, яку ви завантажите спочатку, перш ніж перевести вас у потрібне місце.

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

Якщо ви довіряєте домену, з якого походять файли cookie, можливо, вам вдасться втекти без спільного користування apache, але ви повинні розуміти, що це може становити ризик XSS, якщо ви не будете обережні.


1

Це робочий біт коду.

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

Тут httpclient - це об’єкт DefaultHttpClient, який ви використовували в запиті HttpGet / HttpPost. Також потрібно переконатись, що це ім’я та значення файлу cookie, які слід вказати

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

setCookie встановить файл cookie для вказаної URL-адреси.


Просто як примітка: ви не можете зберігати сеансові файли cookie за допомогою CookieManager.setCookie
John Doe

я не зрозумів, що ... можеш пояснити?
дитина-дроїд

1

Я чарівним чином вирішив усі свої проблеми з файлами cookie за допомогою цього одного рядка в onCreate:

CookieHandler.setDefault(new CookieManager());

редагувати: сьогодні він перестав працювати. :( що за лайна, андроїд.


отже, будь-які оновлення? чому це перестало працювати?, ти це вирішив?
Франсіско Корралес Моралес,

1

Зіткнувся і з цим. Ось що я зробив.

У моїй LoginActivity, всередині мого AsyncTask, я маю таке:

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

// ДЕ CookieStoreHelper.sessionCookie - це інший клас, що містить змінну sessionCookie, визначену як Список файлів cookie; і cookieStore визначають як BasicCookieStore cookieStore;

Тоді на моєму фрагменті, де знаходиться мій WebView, я маю таке:

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

всередині мого методу або безпосередньо перед тим, як ви встановлюєте 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
 }

Швидка порада: встановіть firebug у firefox або скористайтеся консоллю розробника на chrome і спершу протестуйте свою веб-сторінку, захопіть файл cookie та перевірте домен, щоб ви могли десь його зберігати та переконатися, що правильно встановили правильний домен.

Редагувати: відредаговано CookieStoreHelper.cookies до CookieStoreHelper.sessionCookie


1

Мій робочий код

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

Для налагодження запиту "cookieManager.setCookie (....);" Я рекомендую вам переглянути вміст бази даних webviewCookiesChromium.db (зберігається в "/data/data/my.app.webview/database") Там Ви можете побачити правильні налаштування.

Вимкнення "cookieManager.removeSessionCookie ();" та / або "cookieManager.removeAllCookie ();"

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

Порівняйте встановлене значення з тими, які встановлено браузером. Налаштуйте запит на встановлення файлів 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"
// These flags I found in the database:
+ "; path=/registration"
+ "; domain=my.app.site.com"

я використовую код вище для файлу cookie магазину у веб-перегляді, але, будь ласка, повідомте мені про шлях. який тут шлях?
Mehul Tank

@MehulTank Параметр path вказує розташування документа. Файл cookie надсилається на сервер, лише якщо шлях відповідає поточному або батьківському розташуванню документа.
Роланд ван дер Лінден

1

Кілька коментарів (принаймні щодо API> = 21), які я дізнався зі свого досвіду і завдав мені головного болю:

  1. httpа httpsURL-адреси різні. Налаштування файлу cookie http://www.example.comвідрізняється від встановлення файлу cookiehttps://www.example.com
  2. Коса риса в кінці URL-адреси також може змінити ситуацію. У моєму випадку це https://www.example.com/працює, але https://www.example.comне працює.
  3. CookieManager.getInstance().setCookieвиконує асинхронну операцію. Отже, якщо ви завантажуєте URL-адресу відразу після встановлення, не гарантується, що файли cookie вже записані. Щоб запобігти несподіваній та нестабільній поведінці, використовуйте CookieManager # setCookie (URL-адреса рядка, значення рядка, зворотний виклик ValueCallback) ( посилання ) і починайте завантаження URL-адреси після виклику зворотного виклику.

Сподіваюся, мої два центи заощадять трохи часу від деяких людей, тож вам не доведеться стикатися з тими ж проблемами, що і я.


наскільки відрізняється Встановлення файлу cookie для example.com відрізняється від встановлення файлу cookie для example.com ?
softmarshmallow

0

Я зіткнувся з тією ж проблемою, і це вирішить цю проблему у всіх версіях для 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

Зауважте, що краще використовувати субдомени замість звичайної URL-адреси. Отже, встановіть .example.comзамість https://example.com/.

Завдяки Джоді Якобусу Гірсу та іншим я написав так:

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

Не використовуйте свою вихідну URL-адресу

Замість:

cookieManager.setCookie(myUrl, cookieString); 

використовуйте його так:

cookieManager.setCookie("your url host", cookieString); 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.