Problema de cookie do Android WebView

88

Eu tenho um servidor que envia ao meu aplicativo Android um cookie de sessão para ser usado para comunicação autenticada. Estou tentando carregar um WebView com uma URL apontando para esse mesmo servidor e estou tentando passar o cookie de sessão para autenticação. Estou observando que funciona intermitentemente, mas não tenho ideia do porquê. Eu uso o mesmo cookie de sessão para fazer outras chamadas no meu servidor e estas nunca falham na autenticação. Eu só observo esse problema quando tento carregar uma URL em um WebView, e isso não acontece todas as vezes. Muito frustrante.

Abaixo está o código que estou usando para fazer isso. Qualquer ajuda será muito apreciada.

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
fonte
consulte esta questão stackoverflow.com/questions/2566485/…
neeraj t

Respostas:

53

Obrigado justingrammens ! Isso funcionou para mim, consegui compartilhar o cookie dentro de minhas solicitações DefaultHttpClient e atividade do 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
fonte
Isso funcionou muito bem para mim. Construí meu URL de cookie da seguinte forma: String url = (cookie.isSecure ()? "Https": "http") + ": //" + cookie.getDomain () + cookie.getPath ();
Heath Borders
obrigado pelo post ... ele me ajudou a implementar o logout do twitter para meu aplicativo ...;)
rahul
você pode dizer o que deve ser escrito no lugar de myapp.cookie
suraj jain
a palavra-chave é: String cookieString = sessionCookie.getName () + "=" + sessionCookie.getValue () + "; domain =" + sessionCookie.getDomain (); cookieManager.setCookie (myapp.domain, cookieString);
Zennichimaro
1
CookieSyncManager está obsoleto agora :(
Misha Akopov
18

Obrigado Android por arruinar meu domingo. . . Aqui está o que consertou meus aplicativos (depois de iniciar seu webview)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

Devo dizer que as respostas acima provavelmente funcionarão, mas na minha situação, no momento em que o Android se tornou v5 +, meu android webview javascript 'apps' morreu.

Jody Jacobus Geers
fonte
1
Oh! Você acabou de salvar meu dia!
Le Minh
14

Solução: 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
fonte
do que você ganha cookie?, eu só fico assim: PHPSESSID=ljfakdjfklasdfaj!, isso é o suficiente?
Francisco Corrales Morales
6
O que é MySession aqui?
Usuário 3
3

Gostaria de salvar esse cookie de sessão como uma preferência e preencher novamente o gerenciador de cookies com ele. Parece que o cookie de sessão não sobreviveu ao reinício da atividade

Bostone
fonte
Devo acrescentar que meu aplicativo faz muitas outras chamadas não WebView em meu servidor que nunca falham na autenticação. Somente quando tento carregar uma URL em um WebView, percebo esse problema. "Cookie sessionCookie = getCookie ();" está recuperando do banco de dados do aplicativo o cookie de sessão que uso para todas as mensagens com meu servidor.
nannerpus
Bem, se você estiver usando o HttpClient para isso, ele tem seu próprio armazenamento de cookies, então se você mantiver a única instância do cliente, seu cookie sobreviverá, mas pode não ter nada a ver com o usado por sua visualização na web
Bostone
Se bem entendi, você está dizendo que o CookieManager retornado por CookieManager.getInstance (); afetará os cookies usados ​​por instâncias de HttpClient, que WebViews não usam. Se isso for verdade, você sabe como posso passar cookies explicitamente para WebViews? Além disso, olhando os documentos do CookieManager, talvez não chamar "cookieManager.setAcceptCookie (true)" esteja me causando problemas? Obrigado pela ajuda, realmente agradeço.
nannerpus
3

Passei a maior parte das três horas trabalhando em um problema muito semelhante. No meu caso, recebi várias chamadas, que fiz para um serviço da web usando um DefaulHttpCliente, em seguida, quis definir a sessão e todos os outros cookies correspondentes no meu WebView.

Não sei se isso vai resolver o seu problema, pois não sei o que seu getCookie()método faz, mas no meu caso na verdade tive que ligar.

cookieManager.removeSessionCookie();

Primeiro, remova o cookie de sessão e depois adicione-o novamente. Eu estava descobrindo que quando tentei definir o JSESSIONIDcookie sem primeiro removê-lo, o valor que eu queria defini-lo não estava sendo salvo. Não tenho certeza se isso irá ajudá-lo em um problema específico, mas pensei em compartilhar o que descobri.

Justin
fonte
por que não CookieManager.getInstance().removeAllCookie ();?
Francisco Corrales Morales
2

Depois de algum tempo pesquisando reuni algumas peças que me fizeram chegar a essa solução. Uma vez que CookieSyncManager se torne obsoleto, esta pode ser a melhor maneira de definir um cookie específico para uma visualização da web em Kotlin hoje em dia, você não precisa de mais nada.

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
fonte
1

Eu tenho uma abordagem diferente de outras pessoas aqui, e é uma abordagem que funciona garantidamente sem lidar com o CookieSyncManager (onde você está à mercê da semântica como "Observe que mesmo o sync () acontece de forma assíncrona").

Basicamente, navegamos até o domínio correto e, em seguida, executamos o javascript do contexto da página para definir cookies para esse domínio (da mesma forma que a própria página faria). Duas desvantagens do método são que pode introduzir um tempo extra de ida e volta devido à solicitação extra de http que você precisa fazer; e se o seu site não tiver o equivalente a uma página em branco, ele pode exibir qualquer URL que você carregar primeiro antes de levá-lo ao lugar certo.

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 você confia no domínio de origem dos cookies, você pode escapar sem o apache commons, mas deve entender que isso pode representar um risco de XSS se você não for cuidadoso.

Patrick Horn
fonte
1

Este é um código funcional.

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

Aqui, httpclient é o objeto DefaultHttpClient que você usou na solicitação HttpGet / HttpPost. Além disso, uma coisa a ter certeza é o nome e o valor do cookie, ele deve ser fornecido

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

setCookie irá definir o cookie para o URL fornecido.

garoto andróide
fonte
Apenas como uma observação: você não pode armazenar cookies de sessão usando CookieManager.setCookie
John Doe
Eu não entendi ... você pode explicar?
garoto andróide de
1

Eu resolvi magicamente todos os meus problemas de cookies com esta linha em onCreate:

CookieHandler.setDefault(new CookieManager());

editar: parou de funcionar hoje. :( que merda, android.

Chani
fonte
então, alguma atualização? porque parou de funcionar?, você resolveu?
Francisco Corrales Morales
1

Também encontrei isso. Aqui está o que eu fiz.

Na minha LoginActivity, dentro da minha AsyncTask, tenho o seguinte:

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 é outra classe que contém a variável sessionCookie definida como List cookies; e cookieStore é definido como BasicCookieStore cookieStore;

Então, em meu Fragment, onde meu WebView está localizado, tenho o seguinte:

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

dentro do meu método ou um pouco antes de configurar o 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
 }

Dica rápida: tenha o firebug instalado no firefox ou use o console do desenvolvedor no Chrome e teste primeiro sua página da web, capture o cookie e verifique o domínio para que possa armazená-lo em algum lugar e certifique-se de que está configurando corretamente o domínio correto.

Editar: CookieStoreHelper.cookies editado para CookieStoreHelper.sessionCookie

Burnok
fonte
1

Meu código de trabalho

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

Para depurar a consulta, "cookieManager.setCookie (....);" Recomendo que você examine o conteúdo do banco de dados webviewCookiesChromium.db (armazenado em "/data/data/my.app.webview/database"). Lá você pode ver as configurações corretas.

Desativando "cookieManager.removeSessionCookie ();" e / ou "cookieManager.removeAllCookie ();"

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

Compare o valor definido com aqueles definidos pelo navegador. Ajuste o pedido de instalação dos cookies antes até que o navegador "flags" não esteja instalado irá caber com o que você decidir. Descobri que uma consulta pode ser "sinalizadores":

// 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
fonte
Eu usei o código acima para armazenar o cookie no modo de exibição da web, mas, por favor, me informe sobre o caminho. qual é o caminho aqui?
Mehul Tank
@MehulTank O parâmetro path especifica a localização do documento. O cookie só é enviado ao servidor se o caminho corresponder ao local do documento atual ou pai.
Roland van der Linden
1

Alguns comentários (pelo menos para APIs> = 21) que descobri por experiência própria e me deram dores de cabeça:

  1. httpe os httpsurls são diferentes. Definir um cookie para http://www.example.comé diferente de definir um cookie parahttps://www.example.com
  2. Uma barra no final do url também pode fazer a diferença. No meu caso https://www.example.com/funciona, mas https://www.example.comnão funciona.
  3. CookieManager.getInstance().setCookieestá executando uma operação assíncrona. Portanto, se você carregar um url logo após configurá-lo, não é garantido que os cookies já tenham sido gravados. Para evitar comportamentos inesperados e instáveis, use o CookieManager # setCookie (String url, String value, ValueCallback callback) ( link ) e comece a carregar o url depois que o callback for chamado.

Espero que meus dois centavos economizem algum tempo de algumas pessoas para que você não tenha que enfrentar os mesmos problemas que eu.

giorgos.nl
fonte
quão diferente é Definir um cookie para example.com é diferente de definir um cookie para example.com ?
uzu
0

Eu enfrentei o mesmo problema e isso vai resolver esse problema em todas as versões do 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
fonte
0

Observe que pode ser melhor usar subdomínios em vez do URL normal. Portanto, defina em .example.comvez de https://example.com/.

Graças a Jody Jacobus Geers e outros, escrevi assim:

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
fonte
-4

Não use seu url bruto

Ao invés de:

cookieManager.setCookie(myUrl, cookieString); 

use-o assim:

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