Problema con las cookies de Android WebView


89

Tengo un servidor que envía a mi aplicación de Android una cookie de sesión que se utilizará para la comunicación autenticada. Estoy intentando cargar un WebView con una URL que apunta a ese mismo servidor y estoy intentando pasar la cookie de sesión para la autenticación. Observo que funciona de forma intermitente, pero no tengo idea de por qué. Utilizo la misma cookie de sesión para hacer otras llamadas en mi servidor y estas nunca fallan en la autenticación. Solo observo este problema cuando intento cargar una URL en un WebView, y no sucede siempre. Muy frustrante.

A continuación se muestra el código que estoy usando para hacer esto. Cualquier ayuda será 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);

Respuestas:


55

¡Gracias justingrammens ! Eso funcionó para mí, logré compartir la cookie dentro de mis solicitudes DefaultHttpClient y actividad 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();
}   

Esto funciono muy bien para mi. Construí mi URL de cookies así: String url = (cookie.isSecure ()? "Https": "http") + ": //" + cookie.getDomain () + cookie.getPath ();
Heath Borders

gracias por la publicación ... me ayudó a implementar el cierre de sesión de Twitter para mi aplicación ...;)
rahul

¿Puedes decir qué se debe escribir en lugar de myapp.cookie?
suraj jain

la palabra clave es: String cookieString = sessionCookie.getName () + "=" + sessionCookie.getValue () + "; dominio =" + sessionCookie.getDomain (); cookieManager.setCookie (myapp.domain, cookieString);
Zennichimaro

1
CookieSyncManager está obsoleto ahora :(
Misha Akopov

18

Gracias Android por arruinarme el domingo. . . Esto es lo que solucionó mis aplicaciones (después de iniciar su vista web)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

Debo decir que las respuestas anteriores probablemente funcionarán, pero en mi situación, en el momento en que Android se convirtió en v5 +, mis 'aplicaciones' javascript de Android Webview murieron.


1
Oh ! ¡Me acabas de salvar el día!
Le Minh

14

Solución: 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");

¿de qué te sacas cookie?, solo me sale como: PHPSESSID=ljfakdjfklasdfaj!, ¿es eso suficiente?
Francisco Corrales Morales

6
¿Qué es MySession aquí?
Usuario3


3

Guardaría esa cookie de sesión como una preferencia y volvería a llenar con fuerza el administrador de cookies con ella. Suena que la cookie de sesión no sobrevive al reinicio de la actividad


Debo agregar que mi aplicación realiza muchas otras llamadas que no son WebView en mi servidor que nunca fallan en la autenticación. Solo cuando intento cargar una URL en WebView, noto este problema. "Cookie sessionCookie = getCookie ();" está recuperando de la base de datos de la aplicación la cookie de sesión que utilizo para todos los mensajes con mi servidor.
nannerpus

Bueno, si está utilizando HttpClient para eso, tiene su propio almacén de cookies, por lo que si mantiene la instancia única del cliente, su cookie sobrevivirá, pero es posible que no tenga nada que ver con la utilizada por su vista web
Bostone

Si te entiendo correctamente, estás diciendo que el CookieManager devuelto por CookieManager.getInstance (); afectará a las cookies utilizadas por instancias de HttpClient, que WebViews no utiliza. Si esto es cierto, ¿sabe cómo puedo pasar cookies explícitamente a WebViews? Además, mirando los documentos de CookieManager, ¿quizás no llamar a "cookieManager.setAcceptCookie (true)" me está causando problemas? Gracias por la ayuda, realmente lo agradezco.
nannerpus

3

Pasé la mayor parte de las 3 horas trabajando en un problema muy similar. En mi caso, tuve una serie de llamadas, que hice a un servicio web usando un DefaulHttpClienty luego quería configurar la sesión y todas las demás cookies correspondientes en mi WebView.

No sé si esto resolverá tu problema, ya que no sé qué hace tu getCookie()método, pero en mi caso tuve que llamar.

cookieManager.removeSessionCookie();

Primero para eliminar la cookie de sesión y luego volver a agregarla. Descubrí que cuando intenté configurar la JSESSIONIDcookie sin eliminarla primero, el valor que quería configurar no se estaba guardando. No estoy seguro de si esto le ayudará en un problema en particular, pero pensé en compartir lo que había encontrado.


¿por qué no CookieManager.getInstance().removeAllCookie ();?
Francisco Corrales Morales

2

Después de un tiempo investigando, reuní algunas piezas que me hicieron llegar a esta solución. Una vez que CookieSyncManager esté obsoleto, esta puede ser la mejor manera de configurar una cookie específica para una vista web en Kotlin hoy en día, no debería necesitar nada más.

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

Tengo un enfoque diferente al de otras personas aquí, y es un enfoque que garantiza el trabajo sin tener que lidiar con CookieSyncManager (donde estás a merced de semánticas como "Tenga en cuenta que incluso sync () ocurre de forma asincrónica").

Básicamente, buscamos el dominio correcto, luego ejecutamos javascript desde el contexto de la página para establecer cookies para ese dominio (de la misma manera que lo haría la página). Dos inconvenientes del método son que puede introducir un tiempo de ida y vuelta adicional debido a la solicitud http adicional que debe realizar; y si su sitio no tiene el equivalente a una página en blanco, puede mostrar la URL que cargue primero antes de llevarlo al lugar correcto.

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

Si confía en el dominio del que provienen las cookies, es posible que pueda escapar sin apache commons, pero debe comprender que esto puede presentar un riesgo XSS si no tiene cuidado.


1

Este es un fragmento de 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();
    }
}

Aquí, httpclient es el objeto DefaultHttpClient que usó en la solicitud HttpGet / HttpPost. También una cosa que debe asegurarse es el nombre y el valor de la cookie, debe proporcionarse

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

setCookie establecerá la cookie para la URL dada.


Solo como una nota: no puede almacenar cookies de sesión usando CookieManager.setCookie
John Doe

No entendí eso ... ¿puedes explicarme?
niño droide

1

Resolví mágicamente todos mis problemas con las cookies con esta línea en onCreate:

CookieHandler.setDefault(new CookieManager());

editar: dejó de funcionar hoy. :( que mierda, android.


entonces, ¿alguna actualización? ¿Por qué dejó de funcionar ?, ¿lo resolviste?
Francisco Corrales Morales

1

También encontré esto. Esto es lo que hice.

En mi LoginActivity, dentro de mi AsyncTask, tengo lo siguiente:

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

// DONDE CookieStoreHelper.sessionCookie es otra clase que contiene la variable sessionCookie definida como List cookies; y cookieStore se definen como BasicCookieStore cookieStore;

Luego, en mi Fragmento, donde se encuentra mi WebView, tengo lo siguiente:

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

dentro de mi método o justo antes de configurar 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
 }

Consejo rápido: instale firebug en firefox o use la consola de desarrollador en Chrome y pruebe primero su página web, capture la cookie y verifique el dominio para que pueda almacenarlo en algún lugar y asegúrese de que está configurando correctamente el dominio correcto.

Editar: CookieStoreHelper.cookies editado a CookieStoreHelper.sessionCookie


1

Mi código de trabajo

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 la consulta, "cookieManager.setCookie (....);" Te recomiendo que revises el contenido de la base de datos webviewCookiesChromium.db (almacenada en "/data/data/my.app.webview/database") Allí puedes ver la configuración correcta.

Deshabilitar "cookieManager.removeSessionCookie ();" y / o "cookieManager.removeAllCookie ();"

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

Compare el valor establecido con los establecidos por el navegador. Ajustar la solicitud de instalación de las cookies antes de que no se instalen "banderas" del navegador se ajustará a lo que Usted decida. Descubrí que una consulta puede ser "banderas":

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

He usado el código anterior para almacenar cookies en la vista web, pero hágamelo saber sobre la ruta. ¿Qué es el camino aquí?
Mehul Tank

@MehulTank El parámetro de ruta especifica la ubicación del documento. La cookie solo se envía al servidor si la ruta coincide con la ubicación del documento actual o principal.
Roland van der Linden

1

Un par de comentarios (al menos para API> = 21) que descubrí por mi experiencia y me dieron dolores de cabeza:

  1. httpy las httpsURL son diferentes. Configurar una cookie para http://www.example.comes diferente a configurar una cookie parahttps://www.example.com
  2. Una barra al final de la URL también puede marcar la diferencia. En mi caso https://www.example.com/funciona pero https://www.example.comno funciona.
  3. CookieManager.getInstance().setCookieestá realizando una operación asincrónica. Por lo tanto, si carga una URL inmediatamente después de configurarla, no se garantiza que las cookies ya se hayan escrito. Para evitar comportamientos inesperados e inestables, use CookieManager # setCookie (URL de cadena, valor de cadena, devolución de llamada de ValueCallback) ( enlace ) y comience a cargar la URL después de que se llame a la devolución de llamada.

Espero que mis dos centavos le ahorren algo de tiempo a algunas personas para que no tenga que enfrentar los mismos problemas que yo.


¿ Qué diferencia hay entre la configuración de una cookie para example.com y la configuración de una cookie para example.com ?
softmarshmallow

0

Me he enfrentado al mismo problema y resolverá este problema en todas las versiones de 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

Tenga en cuenta que puede ser mejor utilizar subdominios en lugar de la URL habitual. Entonces, establezca en .example.comlugar dehttps://example.com/ .

Gracias a Jody Jacobus Geers y otros escribí así:

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

No uses tu URL sin procesar

En vez de:

cookieManager.setCookie(myUrl, cookieString); 

úsalo así:

cookieManager.setCookie("your url host", cookieString); 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.