Проблема с файлами 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 t

Ответы:


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

спасибо за пост ... это помогло мне реализовать выход из твиттера для моего приложения ...;)
rahul


ключевое слово: String cookieString = sessionCookie.getName () + "=" + sessionCookie.getValue () + "; domain =" + sessionCookie.getDomain (); cookieManager.setCookie (myapp.domain, cookieString);
Зенничимаро

1
CookieSyncManager устарел :(
Миша Акопов

18

Спасибо Android за испорченное воскресенье. . . Вот что исправило мои приложения (после того, как вы запустите свой веб-просмотр)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

Я должен сказать, что приведенные выше ответы, вероятно, сработают, но в моей ситуации, когда Android перешел на версию 5 +, мои приложения javascript для android webview умерли.


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?
User3


3

Я бы сохранил этот файл cookie сеанса в качестве предпочтения и принудительно повторно заселил им диспетчер файлов cookie. Похоже, что cookie сеанса не переживает перезапуск активности


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

Что ж, если вы используете HttpClient для этого, у него есть собственное хранилище файлов cookie, поэтому, если вы сохраните единственный экземпляр клиента, ваш файл cookie сохранится, но он может не иметь ничего общего с тем, который используется вашим веб-представлением
Bostone,

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

3

Я потратил большую половину из трех часов, работая над очень похожей проблемой. В моем случае у меня было несколько вызовов, которые я сделал веб-сервису, используя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 (где вы находитесь во власти семантики типа «Обратите внимание, что даже sync () происходит асинхронно»).

По сути, мы переходим к правильному домену, а затем выполняем 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 commons, но вы должны понимать, что это может представлять риск 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
Джон Доу,

я не понял ... ты можешь объяснить?
дроид-ребенок

1

Я волшебным образом решил все свои проблемы с файлами cookie с помощью этой строки в onCreate:

CookieHandler.setDefault(new CookieManager());

изменить: сегодня он перестал работать. :( какая хрень, android.


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

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, определенную как List cookies; и 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отличается от установки cookie дляhttps://www.example.com
  2. Косая черта в конце URL-адреса также может иметь значение. В моем случае https://www.example.com/работает, но https://www.example.comне работает.
  3. CookieManager.getInstance().setCookieвыполняет асинхронную операцию. Таким образом, если вы загрузите URL-адрес сразу после его установки, не гарантируется, что файлы cookie уже будут записаны. Чтобы предотвратить неожиданное и нестабильное поведение, используйте CookieManager # setCookie (String url, String value, ValueCallback callback) ( ссылка ) и начните загружать 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.