简体   繁体   English

在Android中维护cookie会话

[英]Maintain cookie session in Android

Okay, I have an android application that has a form in it, two EditText, a spinner, and a login button. 好的,我有一个Android应用程序,里面有一个表单,两个EditText,一个微调器和一个登录按钮。 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. 数据通过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. 我在实施cookie解决方案方面取得了成功。 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,它不适用于此设置。

Any suggestions? 有什么建议?

You need to keep the cookie from one call to another. 您需要将cookie从一个调用保持到另一个调用。 Instead of creating a new DefaultHttpClient, use this builder: 使用此构建器,而不是创建新的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;
}

And keep the Builder class as a field of your application. 并将Builder类作为应用程序的一个字段。

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 . 或者从类似的问题中尝试这个想法。

Use this in url login Activity 在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();
    }

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) removeSessionCookie()问题android(code.google。,com)

it says that removeSessionCookie() is implemented in a thread, so whenever it is called; 它说removeSessionCookie()是在一个线程中实现的,所以无论何时调用它; a thread starts and after your setCookie(url, cookieString); 一个线程在你的setCookie(url, cookieString);之后开始setCookie(url, cookieString); is called, it removes the new cookie you just set. 被调用,它会删除你刚刚设置的新cookie。 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()已经执行,而对于某些设备,它会删除cookie,我们就会遇到这个问题。

I suggest you remove this removeSessionCookie(); 我建议你删除这个removeSessionCookie(); as you are setting only one cookie, so it won't conflict with other cookies. 因为您只设置一个cookie,所以它不会与其他cookie冲突。 Your code will work seamlessly. 您的代码将无缝地工作。

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. Sever使用存储在cookie中的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. 换句话说,我GUESS,facebook和twitter的方式,你将删除所有与会话相关的代码,它的服务器使用Access Token来识别用户。

Am i clear? 我明白了吗?

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. 几个星期前我有这个类似的问题,那是因为每次单击按钮时都会创建新的DefaultHttpClient。尝试创建一个DefaultHttpClient,并为您尝试发送的每个请求使用相同的DefaultHttpClient。 it solved my problem 它解决了我的问题

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

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. 当启动任何新活动时(因此我假设在启动新的webview时这是相同的),它实际上是从头开始新的程序。 This new activity will not have access to data from any previous activity (unless that data is passed by being attached to the intent). 此新活动将无法访问任何先前活动中的数据(除非通过附加到意图传递该数据)。

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 a)将更复杂的结构包装在实现的更复杂的类中
    Parcelable interface, which can be stored in an extra. Parcelable接口,可以存储在额外的。

    b) Wrap the more complex structure in a class that implements the b)将更复杂的结构包装在实现的更复杂的类中
    Serializable interface, which can be stored in an extra. 可序列化的接口,可以存储在额外的。

Either of these approaches is fairly complicated and a fair bit of work. 这些方法中的任何一种都相当复杂并且需要相当多的工作。

2)Personally I much prefer the approach suggested by rds. 2)我个人更喜欢rds建议的方法。 To clarify, when rds says: 澄清一下,当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/7746 You 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的内容(尽管我确信您会在某些时候发现需要)并专注于扩展应用程序类的部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM