在 Android 中维护 cookie 会话

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3039137/
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 08:25:53  来源:igfitidea点击:

Maintain cookie session in Android

androidsessioncookiesloginwebview

提问by datguywhowanders

Okay, I have an android application that has a form in it, two EditText, a spinner, and a login button. The user selects the service from the spinner, types in their user name and password, and clicks login. The data is sent via POST, a response is returned, it's handled, a new WebView is launched, the html string generated from the response is loaded, and I have the home page of whatever service the user selected.

好的,我有一个 android 应用程序,其中包含一个表单、两个 EditText、一个微调器和一个登录按钮。用户从微调器中选择服务,输入他们的用户名和密码,然后单击登录。数据通过 POST 发送,返回响应,对其进行处理,启动新的 WebView,加载从响应生成的 html 字符串,并且我拥有用户选择的任何服务的主页。

That's all well and good. Now, when the user clicks on a link, the login info can't be found, and the page asks the user to login again. My login session is being dropped somewhere, and I'm not certain how to pass the info from the class that controls the main part of my app to the class that just launches the webview activity.

这一切都很好。现在,当用户点击链接时,无法找到登录信息,页面要求用户再次登录。我的登录会话被丢弃在某个地方,我不确定如何将信息从控制我的应用程序主要部分的类传递到刚刚启动 webview 活动的类。

The onClick handler from the form login button:

表单登录按钮的 onClick 处理程序:

private class FormOnClickListener implements View.OnClickListener {

    public void onClick(View v) {

        String actionURL, user, pwd, user_field, pwd_field;

        actionURL = "thePageURL";
        user_field = "username"; //this changes based on selections in a spinner
        pwd_field = "password"; //this changes based on selections in a spinner
        user = "theUserLogin";
        pwd = "theUserPassword";

        List<NameValuePair> myList = new ArrayList<NameValuePair>();
        myList.add(new BasicNameValuePair(user_field, user)); 
        myList.add(new BasicNameValuePair(pwd_field, pwd));

        HttpParams params = new BasicHttpParams();
        DefaultHttpClient client = new DefaultHttpClient(params);
        HttpPost post = new HttpPost(actionURL);
        HttpResponse response = null;
        BasicResponseHandler myHandler = new BasicResponseHandler();
        String endResult = null;

        try { post.setEntity(new UrlEncodedFormEntity(myList)); } 
        catch (UnsupportedEncodingException e) { e.printStackTrace(); } 

        try { response = client.execute(post); } 
        catch (ClientProtocolException e) { e.printStackTrace(); } 
        catch (IOException e) { e.printStackTrace(); }  

        try { endResult = myHandler.handleResponse(response); } 
        catch (HttpResponseException e) { e.printStackTrace(); } 
        catch (IOException e) { e.printStackTrace(); }

        List<Cookie> cookies = client.getCookieStore().getCookies();
        if (!cookies.isEmpty()) {
            for (int i = 0; i < cookies.size(); i++) {
                cookie = cookies.get(i);
            }
        }

       Intent myWebViewIntent = new Intent(MsidePortal.this, MyWebView.class);
       myWebViewIntent.putExtra("htmlString", endResult);
       myWebViewIntent.putExtra("actionURL", actionURL);
       startActivity(myWebViewIntent);
    }
}

And here is the WebView class that handles the response display:

这是处理响应显示的 WebView 类:

public class MyWebView extends android.app.Activity {

    private class MyWebViewClient extends WebViewClient {

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.web);

        MyWebViewClient myClient = new MyWebViewClient();
        WebView webview = (WebView)findViewById(R.id.mainwebview);
        webview.getSettings().setBuiltInZoomControls(true); 
        webview.getSettings().setJavaScriptEnabled(true); 
        webview.setWebViewClient(myClient);

        Bundle extras = getIntent().getExtras();
        if(extras != null) 
        {
            // Get endResult
            String htmlString = extras.getString("htmlString");
            String actionURL = extras.getString("actionURL");

            Cookie sessionCookie = MsidePortal.cookie;
            CookieSyncManager.createInstance(this);
            CookieManager cookieManager = CookieManager.getInstance();
            if (sessionCookie != null) {
                cookieManager.removeSessionCookie();
                String cookieString = sessionCookie.getName()
                        + "=" + sessionCookie.getValue()
                        + "; domain=" + sessionCookie.getDomain();
                cookieManager.setCookie(actionURL, cookieString);
                CookieSyncManager.getInstance().sync();
            }  

            webview.loadDataWithBaseURL(actionURL, htmlString, "text/html", "utf-8", actionURL);}
        }
    }
}

I've had mixed success implementing that cookie solution. It seems to work for one service I log into that I know keeps the cookies on the server (old, archaic, but it works and they don't want to change it.) The service I'm attempting now requires the user to keep cookies on their local machine, and it does not work with this setup.

我在实施该 cookie 解决方案方面取得了不同的成功。它似乎适用于我登录的一项服务,我知道我知道将 cookie 保留在服务器上(旧的、过时的,但它可以工作并且他们不想更改它。)我现在尝试的服务要求用户保留cookie 在他们的本地机器上,并且它不适用于此设置。

Any suggestions?

有什么建议?

回答by rds

You need to keep the cookie from one call to another. Instead of creating a new DefaultHttpClient, use this builder:

您需要保持 cookie 从一个调用到另一个调用。使用此构建器,而不是创建新的 DefaultHttpClient:

private Object mLock = new Object();
private CookieStore mCookie = null;
/**
 * Builds a new HttpClient with the same CookieStore than the previous one.
 * This allows to follow the http session, without keeping in memory the
 * full DefaultHttpClient.
 * @author Régis Décamps <[email protected]>
 */
private HttpClient getHttpClient() {
        final DefaultHttpClient httpClient = new DefaultHttpClient();
        synchronized (mLock) {
                if (mCookie == null) {
                        mCookie = httpClient.getCookieStore();
                } else {
                        httpClient.setCookieStore(mCookie);
                }
        }
        return httpClient;
}

And keep the Builder class as a field of your application.

并将 Builder 类作为应用程序的一个字段。

回答by suraj jain

Use this in url login Activity

在 url 登录活动中使用它

    List<Cookie> cookies = client.getCookieStore().getCookies();
    if (!cookies.isEmpty()) {
        for (int i = 0; i < cookies.size(); i++) {
            cookie = cookies.get(i);
        }
    }

    Cookie sessionCookie = cookie;

    if (sessionCookie != null) {
        String cookieString = sessionCookie.getName() + "="
                + sessionCookie.getValue() + "; domain="
                + sessionCookie.getDomain();
        cookieManager
                .setCookie("www.mydomain.com", cookieString);
        CookieSyncManager.getInstance().sync();
    }

回答by Darpan

You have used this line -

你用过这条线——

    if (sessionCookie != null) {
        cookieManager.removeSessionCookie();
    }

To ensure you receive new cookie everytime.

确保您每次都能收到新的 cookie。

Seems like you have gone through same issue as I faced, check below link -

好像你遇到了和我一样的问题,请查看下面的链接 -

removeSessionCookie() issue of android (code.google.,com)

android (code.google.,com) 的 removeSessionCookie() 问题

it says that removeSessionCookie()is implemented in a thread, so whenever it is called; a thread starts and after your setCookie(url, cookieString);is called, it removes the new cookie you just set. So for some devices it works well as removeSessionCookie()is already executed, while, for some, it remove the cookie, and we get that problem.

它说它removeSessionCookie()是在一个线程中实现的,所以每当它被调用时;一个线程启动,在您setCookie(url, cookieString);被调用后,它会删除您刚刚设置的新 cookie。因此,对于某些设备,它removeSessionCookie()在已经执行的情况下运行良好,而对于某些设备,它删除了 cookie,我们遇到了这个问题。

I suggest you remove this removeSessionCookie();as you are setting only one cookie, so it won't conflict with other cookies. Your code will work seamlessly.

我建议你删除它,removeSessionCookie();因为你只设置了一个 cookie,所以它不会与其他 cookie 冲突。您的代码将无缝运行。

回答by tsingroo

Sever use the sessionID stored in the cookies to identify the specfic user.Every language has a different sessionID name in the http headers.You can use some network tool or browser to see what is the name the sessionID called.

服务器使用cookies中存储的sessionID来识别特定用户。每种语言在http头中都有不同的sessionID名称。您可以使用一些网络工具或浏览器来查看sessionID调用的名称是什么。

And other way,I GUESS,the facebook and twitter way,you will remove all the session-related code, it's server use Access Token to identify a user.

和其他方式,我猜,facebook 和 twitter 方式,你将删除所有与会话相关的代码,它的服务器使用访问令牌来识别用户。

Am i clear?

我清楚吗?

回答by Dan

When any new activity is launched (so I assume this is the same when you launch a new webview) it is effectively launching a new program from scratch. This new activity will not have access to data from any previous activity (unless that data is passed by being attached to the intent).

当启动任何新活动时(所以我假设当你启动一个新的 webview 时这是一样的)它实际上是从头开始启动一个新程序。这个新活动将无法访问来自任何先前活动的数据(除非通过附加到意图来传递数据)。

2 possible solutions:

2种可能的解决方案:

1) putExtra can only be used to pass primitive data, so to pass something more complex you need to either

1) putExtra 只能用于传递原始数据,因此要传递更复杂的东西,您需要

  • a) Wrap the more complex structure in a class that implements the
    Parcelable interface, which can be stored in an extra.

    b) Wrap the more complex structure in a class that implements the
    Serializable interface, which can be stored in an extra.

  • a) 将更复杂的结构包装在一个实现
    Parcelable 接口的类中,该类可以存储在一个 extra 中。

    b) 将更复杂的结构包装在一个实现
    Serializable 接口的类中,该类可以存储在一个额外的。

Either of these approaches is fairly complicated and a fair bit of work.

这两种方法中的任何一种都相当复杂并且需要大量工作。

2)Personally I much prefer the approach suggested by rds. To clarify, when rds says:

2)我个人更喜欢 rds 建议的方法。澄清一下,当 rds 说:

keep the Builder class as a field of your application.

将 Builder 类作为应用程序的一个字段。

I think he means extend the application class. Any properties stored there are available globally to all activities. This article explains very clearly how to do this: http://www.screaming-penguin.com/node/7746You can ignore the stuff about the AsyncTask (although I'm sure you will find a need for that at some point) and just concentrate on the part about extending the application class.

我认为他的意思是扩展应用程序类。存储在那里的任何属性对所有活动都是全局可用的。这篇文章非常清楚地解释了如何做到这一点:http: //www.screaming-penguin.com/node/7746你可以忽略关于 AsyncTask 的东西(虽然我相信你会发现在某个时候需要它)并专注于扩展应用程序类的部分。

回答by Christoph Haefner

You could store the cookies in a shared preference and load them as needed in other activitys.

您可以将 cookie 存储在共享首选项中,并根据需要在其他活动中加载它们。

Or try this idea from a similar question.

或者从一个类似的问题尝试这个想法。

回答by Hendra

I have this similiar problem several week ago, that is because you create new DefaultHttpClient each time you click the button.. try create one DefaultHttpClient, and using the same DefaultHttpClient for each request you trying to send. it solved my problem

几周前我遇到了这个类似的问题,那是因为每次单击按钮时都会创建新的 DefaultHttpClient.. 尝试创建一个 DefaultHttpClient,并为您尝试发送的每个请求使用相同的 DefaultHttpClient。它解决了我的问题

回答by guleryuz

dunno if you still need an answer, but again here comes some additional info that may help

if you want to keep cookies sync'ed

不知道您是否仍然需要答案,但是

如果您想保持 cookie 同步,这里有一些额外的信息可能会有所帮助

// ensure any cookies set by the dialog are saved
CookieSyncManager.getInstance().sync();

and if you want to clear Cookies

如果你想清除 Cookies

public static void clearCookies(Context context) {
        // Edge case: an illegal state exception is thrown if an instance of 
        // CookieSyncManager has not be created.  CookieSyncManager is normally
        // created by a WebKit view, but this might happen if you start the 
        // app, restore saved state, and click logout before running a UI 
        // dialog in a WebView -- in which case the app crashes
        @SuppressWarnings("unused")
        CookieSyncManager cookieSyncMngr = 
            CookieSyncManager.createInstance(context);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();
    }