Android WebView Cookie 问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1652850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:25:19  来源:igfitidea点击:

Android WebView Cookie Problem

androidcookieswebview

提问by nannerpus

I have a server that sends my android app a session cookie to be used for authenticated communication. I am trying to load a WebView with a URL pointing to that same server and I'm trying to pass in the session cookie for authentication. I am observing that it works intermittently but I have no idea why. I use the same session cookie to make other calls on my server and these never fail authentication. I only observe this problem when trying to load a URL in a WebView, and it does not happen every time. Very frustrating.

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

Below is the code that I'm using to do this. Any help will be greatly appreciated.

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

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

回答by k7k0

Thanks justingrammens! That worked for me, I managed to share the cookie within my DefaultHttpClient requests and WebView activity:

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

回答by Jody Jacobus Geers

Thanks Android for ruining my Sunday . . . Heres what fixed my Apps ( after you init your webview )

感谢 Android 毁了我的星期天。. . 这是修复我的应用程序的内容(在您初始化您的 webview 之后)

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

  CookieManager cookieManager = CookieManager.getInstance();

  cookieManager.setAcceptThirdPartyCookies( webView, true );

}

I should say the above answers will probably work but in my situation the moment Android went v5+ my android webview javascript 'apps' died.

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

回答by Sanket

Solution:Webview CookieSyncManager

解决方案: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");

回答by Jan Muller

the solution is to give the Android enough time to proccess cookies. You can find more information here: http://code.walletapp.net/post/46414301269/passing-cookie-to-webview

解决方案是给 Android 足够的时间来处理 cookie。您可以在此处找到更多信息:http: //code.walletapp.net/post/46414301269/passing-cookie-to-webview

回答by Bostone

I would save that session cookie as a preference and forcefully repopulate the cookie manager with it. It sounds that session cookie in not surviving Activity restart

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

回答by Justin

I've spent the greater half of 3 hours working on a very similar issue. In my case I had a number of calls, that I made to a web service using a DefaulHttpClientand then I wanted to set the session and all other corresponding cookies in my WebView.

我花了 3 个小时的大部分时间来研究一个非常相似的问题。就我而言,我有许多调用,我使用 a 对 Web 服务进行了调用,DefaulHttpClient然后我想在我的WebView.

I don't know if this will solve your problem, since I don't know what your getCookie()method does, but in my case I actually had to call.

我不知道这是否能解决您的问题,因为我不知道您的getCookie()方法是做什么的,但就我而言,我实际上不得不打电话。

cookieManager.removeSessionCookie();

First to remove the session cookie and then re-add it. I was finding that when I tried to set the JSESSIONIDcookie without first removing it, the value I wanted to set it to wasn't being save. Not sure if this will help you particular issue, but thought I'd share what I had found.

首先删除会话cookie,然后重新添加它。我发现当我试图在JSESSIONID没有先删除它的情况下设置cookie 时,我想设置它的值没有被保存。不确定这是否会帮助您解决特定问题,但我想我会分享我的发现。

回答by Samuel Luís

After some time researching I've gathered some pieces that made me get to this solution. Once that CookieSyncManager is deprecated, this may be the best way to set a specific cookie for a webview in Kotlin nowadays, you shouldn't need anything else.

经过一段时间的研究,我收集了一些让我得到这个解决方案的片段。一旦 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)
}

回答by giorgos.nl

Couple of comments (at least for APIs >= 21) which I found out from my experience and gave me headaches:

我从经验中发现的一些评论(至少对于 API >= 21)让我头疼:

  1. httpand httpsurls are different. Setting a cookie for http://www.example.comis different than setting a cookie for https://www.example.com
  2. A slash in the end of the url can also make a difference. In my case https://www.example.com/works but https://www.example.comdoes not work.
  3. CookieManager.getInstance().setCookieis performing an asynchronous operation. So, if you load a url right away after you set it, it is not guaranteed that the cookies will have already been written. To prevent unexpected and unstable behaviours, use the CookieManager#setCookie(String url, String value, ValueCallback callback) (link) and start loading the url after the callback will be called.
  1. httphttps网址不同。设置 cookiehttp://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。

I hope my two cents save some time from some people so you won't have to face the same problems like I did.

我希望我的两分钱可以节省一些人的时间,这样你就不必像我一样面临同样的问题。

回答by Vadim.Ivanov

My working code

我的工作代码

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

To debug the query, "cookieManager.setCookie (....);" I recommend you to look through the contents of the database webviewCookiesChromium.db (stored in "/data/data/my.app.webview/database") There You can see the correct settings.

调试查询,“cookieManager.setCookie(....);” 我建议您查看数据库 webviewCookiesChromium.db 的内容(存储在“/data/data/my.app.webview/database”中)在那里可以看到正确的设置。

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

禁用“cookieManager.removeSessionCookie();” 和/或“cookieManager.removeAllCookie();”

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

Compare the set value with those that are set by the browser. Adjust the request for the installation of the cookies before until "flags" browser is not installed will fit with what You decide. I found that a query can be "flags":

将设置值与浏览器设置的值进行比较。在“标记”浏览器未安装之前调整安装 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"

回答by Patrick Horn

I have a different approach from other people here, and it an approach that is guaranteed work without dealing with the CookieSyncManager (where you are at the mercy of semantics like "Note that even sync() happens asynchronously").

我有一种与这里其他人不同的方法,它是一种保证工作而不处理 CookieSyncManager 的方法(在这种情况下,您受语义的支配,例如“请注意,即使 sync() 也是异步发生的”)。

Essentially, we browse to the correct domain, then we execute javascript from the page context to set cookies for that domain (the same way the page itself would). Two drawbacks to the method are that may introduce an extra round trip time due to the extra http request you have to make; and if your site does not have the equivalent of a blank page, it may flash whatever URL you load first before taking you to the right place.

本质上,我们浏览到正确的域,然后我们从页面上下文中执行 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;
            }
        }
    }
}

If you trust the domain the cookies are from, you may be able to get away without apache commons, but you have to understand that this can present a XSS risk if you are not careful.

如果您信任 cookie 来自的域,您也许可以在没有 apache commons 的情况下逃脱,但您必须明白,如果您不小心,这可能会带来 XSS 风险。