在Android中维护cookie会话

17

我有一个包含表单、两个EditText、一个Spinner和一个登录按钮的Android应用程序。用户从Spinner中选择服务,输入他们的用户名和密码,然后点击登录。数据通过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,并且此设置无法正常工作。

有什么建议吗?

8个回答

7

您需要在每个请求之间保留 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的更新后的答案? - Sandeep Yohans
1
答案针对Apache HttpClient(客户端)而言。是的,自Marshmallow以来,HttpClient已被弃用。https://developer.android.com/about/versions/marshmallow/android-6.0-changes 在此回答java.net类的相同问题将完全离题。 - rds
你能指导我一些资源或片段,以最新的API在我的项目中实现此解决方案吗? - Sandeep Yohans

1

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

0

不知道你是否还需要答案,但这里有一些额外的信息可能会有所帮助

如果你想要保持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();
    }

0

你使用了这行代码 -

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

为确保您每次都收到新的 cookie。
看起来您遇到了我曾经面临的同样问题,请查看下面的链接 -
{{link1:android(code.google.com)上removeSessionCookie()问题}}
它说removeSessionCookie()是在一个线程中实现的,所以每次调用它时,一个线程就会启动,并且在调用setCookie(url,cookieString);后,它会删除您刚设置的新 cookie。因此,对于一些设备,它可以正常工作,因为removeSessionCookie()已经执行,而对于一些设备,则会删除 cookie,我们就会遇到问题。
我建议您删除这个removeSessionCookie();,因为您只设置了一个 cookie,所以不会与其他 cookie 冲突。您的代码将无缝运行。

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

2
当然可以,但是如果你只需要 cookie,存储整个 DefaultHttpClient 就会浪费内存。 - rds

0
服务器使用存储在cookie中的sessionID来识别特定用户。每种语言在http头中都有不同的sessionID名称。您可以使用一些网络工具或浏览器查看sessionID的名称是什么。
另外一种方式,我猜想,就像Facebook和Twitter那样,您将删除所有与session相关的代码,而是使用Access Token来识别用户。
我表达清楚了吗?

0

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

或者尝试来自类似问题的这个想法。


0

当启动任何新活动时(因此我认为当您启动新的Web视图时也是如此),它实际上是从头开始启动一个新程序。这个新活动将无法访问来自任何先前活动的数据(除非该数据通过附加到意图中进行传递)。

2种可能的解决方案:

1)putExtra只能用于传递原始数据,因此要传递更复杂的内容,您需要执行以下操作之一

  • a)将更复杂的结构包装在实现Parcelable接口的类中,该类可以存储在额外的位置。

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

这两种方法都相当复杂且需要大量工作。

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接