Android WebView Cookie问题


89

我有一台服务器,向我的android应用发送一个会话cookie,用于身份验证通信。我正在尝试使用指向同一服务器的URL加载WebView,并且试图传递会话cookie以进行身份​​验证。我观察到它间歇性地工作,但是我不知道为什么。我使用相同的会话cookie在服务器上进行其他调用,而这些调用永远不会失败。我只在尝试在WebView中加载URL时才观察到此问题,并且并非每次都会发生。非常沮丧。

以下是我用来执行此操作的代码。任何帮助将不胜感激。

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

Answers:


55

谢谢justingrammens!这对我有用,我设法在DefaultHttpClient请求和WebView活动中共享cookie:

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

这对我来说很棒。我这样构造了我的cookie url:字符串url =(cookie.isSecure()?“ https”:“ http”)+“://” + cookie.getDomain()+ cookie.getPath();
Heath Borders

感谢您的帖子...它帮助我实现了我的应用程序的Twitter注销...;)
rahul

您能告诉我要写些什么代替myapp.cookie吗?
Suraj Jain 2012年

关键字是:字符串cookieString = sessionCookie.getName()+“ =” + sessionCookie.getValue()+“; domain =” + sessionCookie.getDomain(); cookieManager.setCookie(myapp.domain,cookieString);
Zennichimaro 2012年

1
CookieSyncManager现在已弃用:(
Misha Akopov '16

18

感谢Android破坏了我的星期天。。。这是修复我的应用程序的原因(在您初始化Webview之后)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

我应该说上述答案可能会起作用,但是在我的情况下,当Android进入v5 +版本时,我的android webview javascript'apps'死了。


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!,够了吗?
Francisco Corrales Morales 2014年

6
什么是MySession?
User3


3

我会将会话cookie保存为首选项,然后用它强制重新填充cookie管理器。听起来会话cookie在无法幸存的Activity中重新启动


我应该补充一点,我的应用程序在服务器调用上进行了许多其他的非WebView调用,这些认证从未失败。仅当我尝试在WebView中加载URL时,我才注意到此问题。“ Cookie sessionCookie = getCookie();” 正在从应用程序的数据库中检索用于服务器所有消息的会话cookie。
nannerpus

好吧,如果您使用HttpClient拥有自己的Cookie存储,那么如果您持有该客户端的单个实例,您的Cookie将会继续存在,但是它可能与您的网络视图所使用的Cookie没有关系
Bostone,

如果我对您的理解正确,那么您是说CookieManager是由CookieManager.getInstance()返回的;将影响HttpClient实例使用的cookie,而WebView则不使用它们。如果是这样,您是否知道如何将Cookie显式传递到WebView?另外,查看CookieManager文档,也许不调用“ cookieManager.setAcceptCookie(true)”会导致我出现问题吗?感谢您的帮助,非常感谢。
nannerpus

3

我花了3个多小时的大部分时间来解决一个非常相似的问题。在我的情况下,我使用a调用了一个Web服务DefaulHttpClient,然后我要设置会话和所有其他相应的cookie WebView

我不知道这是否可以解决您的问题,因为我不知道您的getCookie()方法能做什么,但就我而言,我实际上必须调用。

cookieManager.removeSessionCookie();

首先删除会话cookie,然后重新添加它。我发现,当我尝试设置JSESSIONIDcookie而没有先将其删除时,我想要将其设置为的值并未保存。不知道这是否会帮助您解决特定的问题,但我想与您分享发现的内容。


为什么不CookieManager.getInstance().removeAllCookie ();呢?
Francisco Corrales Morales 2014年

2

经过一段时间的研究,我收集了一些使我着手解决方案的作品。一旦弃用CookieSyncManager,这可能是当今在Kotlin中为Webview设置特定Cookie的最佳方法,您不需要任何其他操作。

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是您在HttpGet / HttpPost请求中使用的DefaultHttpClient对象。同样要确保Cookie名称和值是一回事,

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

setCookie将设置给定URL的cookie。


请注意:您不能使用CookieManager.setCookie存储会话cookie
John Doe

我没听懂...你能解释一下吗?
droid kid

1

我用onCreate中的这一行神奇地解决了我所有的cookie问题:

CookieHandler.setDefault(new CookieManager());

编辑:它今天停止工作。:(什么废话,android。


那么,有什么更新吗?为什么它停止工作?您解决了吗?
弗朗西斯科·科拉莱斯·莫拉莱斯2014年

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

// Where CookieStoreHelper.sessionCookie是另一个类,包含定义为List cookie的sessionCookie变量;和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
 }

快速提示:在firefox上安装了firebug或在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 '18

@MehulTank path参数指定文档位置。仅当路径与当前或父文档位置匹配时,cookie才会发送到服务器。
罗兰·范德·林登

1

我从经验中发现了一些意见(至少对于API> = 21而言),并让我头疼:

  1. httphttps网址不同。为设置Cookie与http://www.example.com为设置Cookie不同https://www.example.com
  2. 网址末尾的斜线也可以有所作为。就我而言,https://www.example.com/有效但https://www.example.com无效。
  3. CookieManager.getInstance().setCookie正在执行异步操作。因此,如果在设置后立即加载URL,则不能保证cookie已经被写入。为了防止意外和不稳定的行为,请使用CookieManager#setCookie(String url,String value,ValueCallback callback)(link)并在调用回调后开始加载URL。

我希望我的两分钱能节省一些人的时间,这样您就不必再像我一样面对同样的问题了。


example.com设置cookie与为example.com设置cookie有何不同?
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.comhttps://example.com/

感谢Jody Jacobus Geers和其他人,我这样写道:

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

不要使用原始网址

代替:

cookieManager.setCookie(myUrl, cookieString); 

像这样使用它:

cookieManager.setCookie("your url host", cookieString); 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.