【问题标题】:Maintain cookie session in Android在 Android 中维护 cookie 会话
【发布时间】:2011-03-03 14:53:27
【问题描述】:

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

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

表单登录按钮的 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);
    }
}

这里是处理响应显示的 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);}
        }
    }
}

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

有什么建议吗?

【问题讨论】:

    标签: android session cookies login webview


    【解决方案1】:

    您需要保持 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 <decamps@users.sf.net>
     */
    private HttpClient getHttpClient() {
            final DefaultHttpClient httpClient = new DefaultHttpClient();
            synchronized (mLock) {
                    if (mCookie == null) {
                            mCookie = httpClient.getCookieStore();
                    } else {
                            httpClient.setCookieStore(mCookie);
                    }
            }
            return httpClient;
    }
    

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

    【讨论】:

    • 您好@rds,我无法在我的 Builder 类中添加此代码,因为您的解决方案中的大多数类已被弃用。你想更新你的答案,还是为当前最新的 api 发布一个新的更新答案?
    • 答案是特定于 Apache HttpClient(客户端是关于)的。是的,自 Marshmallow 以来,HttpClient 已被弃用。 developer.android.com/about/versions/marshmallow/… 在这里为java.net 类回答同样的问题完全是题外话。
    • 你能指导我一些资源或sn-p用最新的api在我的项目中实现这个解决方案吗?
    【解决方案2】:

    在url登录Activity中使用这个

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

    【讨论】:

      【解决方案3】:

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

      或从similar question 尝试这个想法。

      【讨论】:

        【解决方案4】:

        不知道您是否仍需要答案,但这里又提供了一些可能会有所帮助的附加信息

        如果您想保持 cookie 同步

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

        如果您想清除 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();
            }
        

        【讨论】:

          【解决方案5】:

          当启动任何新活动时(所以我假设当您启动新的 web 视图时也是如此),它实际上是从头开始启动一个新程序。此新活动将无法访问任何先前活动的数据(除非该数据是通过附加到意图来传递的)。

          2 种可能的解决方案:

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

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

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

          这两种方法都相当复杂,而且工作量很大。

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

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

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

            • 当然可以,但是当您只需要 cookie 时,存储整个 DefaultHttpClient 会浪费内存。
            【解决方案7】:

            你用过这条线——

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

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

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

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

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

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

            【讨论】:

              【解决方案8】:

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

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

              我说清楚了吗?

              【讨论】:

                猜你喜欢
                • 2014-10-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-03-28
                • 2014-12-08
                • 1970-01-01
                相关资源
                最近更新 更多