Android:使用 DefaultHttpClient 登录网站并保留会话/cookie

Posted

技术标签:

【中文标题】Android:使用 DefaultHttpClient 登录网站并保留会话/cookie【英文标题】:Android: log into website and preserve session/cookie using DefaultHttpClient 【发布时间】:2010-08-08 13:46:23 【问题描述】:

我浏览过不同的教程和这个网站,但找不到合适的解决方案。另一方面,我已经看到应用程序登录网站并请求更多信息,所以我确信有办法让它工作,但也许我的方法全错了。

这就是我想要做的事情:我想登录一个需要用户身份验证的网站,然后读取和解析只有在用户登录后才能访问的网站。 问题:在将凭据发布到网站后,我收到了一个似乎没有保存在我的 HttpClient 中的 cookie,即使文档表明应该发生这种情况。

这是我的一些代码:

DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httpost = new HttpPost(LOGIN_URL);

List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair(USER_FIELD, login));
nvps.add(new BasicNameValuePair(PASS_FIELD, pw));
nvps.add(new BasicNameValuePair(REMEMBERME, "on"));

httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

HttpResponse response = httpclient.execute(httpost);
HttpEntity entity = response.getEntity();

if (entity != null) 
  entity.consumeContent();


List<Cookie> cookies = httpclient.getCookieStore().getCookies();

当我输出“cookies”的内容时,一切似乎都很好(我收到了一个会话):

- [version: 0][name: ASP.NET_SessionId][value: xxx][domain: xxx][path: /][expiry: null]

据我了解,只要我不关闭 cookie/会话,它将被保留并在我的 HttpClient 中使用。

阅读下一页时(受限),使用此代码:

HttpGet httpget2 = new HttpGet(RESTRICTED_URL);
response = httpclient.execute(httpget2);
entity = response.getEntity();
InputStream data = entity.getContent();
// data will be parsed here
if (entity != null) 
    entity.consumeContent();

// connection will be closed afterwards

如果我输出 GET 请求的响应(使用 response.getStatusLine()),我会收到“200 OK”消息,但解析返回的站点显示,登录丢失(我只检索登录表单) .

感谢任何帮助。

【问题讨论】:

【参考方案1】:

在我必须登录的应用程序中。首先,我必须运行 GET,然后运行 ​​POST,然后再次运行 GET。第一次获取将为我的连接实例化一个 Jsession Id。 POST 将验证我的 ID,然后原始 get GET 将返回真实内容。

下面的代码是在 JBoss 中运行的应用程序

public boolean login() 
    HttpGet  httpGet = new HttpGet(  "http://localhost:8080/gwt-console-server/rs/identity/secure/sid/");
    HttpPost httpPost = new HttpPost("http://localhost:8080/gwt-console-server/rs/identity/secure/j_security_check");
    HttpResponse response = null;

    List<NameValuePair> nvps = new ArrayList<NameValuePair>();
    nvps.add(new BasicNameValuePair(USER_FIELD, userName));
    nvps.add(new BasicNameValuePair(PASS_FIELD, password));

    try 
        httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        response = httpClient.execute(httpGet);
        EntityUtils.consume(response.getEntity());

        response = httpClient.execute(httpPost);
        EntityUtils.consume(response.getEntity());

        response = httpClient.execute(httpGet);
        String sessionId =EntityUtils.toString(response.getEntity());

        String cookieId =""; 
        List<Cookie> cookies = ((AbstractHttpClient) httpClient).getCookieStore().getCookies();
        for (Cookie cookie: cookies)
            if (cookie.getName().equals("JSESSIONID"))
                cookieId = cookie.getValue();
            
        

        if(sessionId!= null && sessionId.equals(cookieId) )
            return true;
        
     catch (ClientProtocolException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
     catch (IOException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
    
    return false;   

【讨论】:

【参考方案2】:

假设您的httpclient 对象在两种情况下都相同,并且假设RESTRICTED_URLLOGIN_URL 在同一个域中,那么我认为您所拥有的应该可以工作。

您可能希望使用 Wireshark 或代理或其他东西来检查您发出的 HTTP 请求,以查看 cookie 是否实际附加到请求中。可能是附加了 cookie,在这种情况下,还有其他错误导致您的第二个请求失败。

【讨论】:

httpclient 对于所有请求都是相同的,并且 URL 都在同一个域上(都没有 SSL)。我会尝试 Wireshark 找出正在发送的内容,谢谢您的提示。 我已经尝试过了:cookie 附加到第二个 (GET) 请求中,我收到一条“302 Found”消息,该消息将显示登录屏幕。 @Select0r: 听起来第二个请求出现了其他问题(例如,服务器期待 Referer: 标头)。 听起来很合理,谢谢。当我使用浏览器登录网站时,我将使用 Wireshark 分析流量,并在发现差异后立即返回此处。 这没有帮助,不幸的是,我必须进一步调查。我认为我尝试登录的域可能使用了更复杂的机制,所以我会先在另一台服务器上使用测试脚本尝试我的脚本。【参考方案3】:

您必须使用单例模式创建DefaultHttpClient httpclient,以便 sessioncookie 使您仍然保持登录会话。

这是Mainactivity 类:

public static DefaultHttpClient httpClient;

@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    RequestPage request = new RequestPage();
    request.post("http://www.example.com/login.php");

    RequestPage requestProfile =new RequestPage();
    requestProfile.post("http://www.example.com/profile.php");

这是RequestPage 类:

private InputStream post(String url)
    String paramUsername = "username";
    String paramPassword = "pass";

    if(MainActivity.httpClient==null)
        MainActivity.httpClient = new DefaultHttpClient();
    
    DefaultHttpClient httpClient = MainActivity.httpClient;

    // In a POST request, we don't pass the values in the URL.
    //Therefore we use only the web page URL as the parameter of the HttpPost argument
    HttpPost httpPost = new HttpPost(url);

            // Because we are not passing values over the URL, we should have a mechanism to pass the values that can be
    //uniquely separate by the other end.
    //To achieve that we use BasicNameValuePair             
    //Things we need to pass with the POST request
    BasicNameValuePair usernameBasicNameValuePair = new BasicNameValuePair("username", paramUsername);
    BasicNameValuePair passwordBasicNameValuePAir = new BasicNameValuePair("password", paramPassword);

    // We add the content that we want to pass with the POST request to as name-value pairs
    //Now we put those sending details to an ArrayList with type safe of NameValuePair
    List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
    nameValuePairList.add(usernameBasicNameValuePair);
    nameValuePairList.add(passwordBasicNameValuePAir);

    try 
        // UrlEncodedFormEntity is an entity composed of a list of url-encoded pairs. 
        //This is typically useful while sending an HTTP POST request. 
        UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(nameValuePairList);

        // setEntity() hands the entity (here it is urlEncodedFormEntity) to the request.
        httpPost.setEntity(urlEncodedFormEntity);

        try 
            // HttpResponse is an interface just like HttpPost.
            //Therefore we can't initialize them
            HttpResponse httpResponse = httpClient.execute(httpPost);

            // According to the JAVA API, InputStream constructor do nothing. 
            //So we can't initialize InputStream although it is not an interface


            return httpResponse.getEntity().getContent();

         catch (ClientProtocolException cpe) 
            System.out.println("First Exception caz of HttpResponese :" + cpe);
            cpe.printStackTrace();
         catch (IOException ioe) 
            System.out.println("Second Exception caz of HttpResponse :" + ioe);
            ioe.printStackTrace();
        

     catch (UnsupportedEncodingException uee) 
        System.out.println("An Exception given because of UrlEncodedFormEntity argument :" + uee);
        uee.printStackTrace();
    

    return null;

【讨论】:

【参考方案4】:

您可以这样做,尽管这是一种解决方法。

@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    WebView webv = (WebView)findViewById(R.id.MainActivity_webview);         
    webv.setWebViewClient(new WebViewClient()
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) 
                view.loadUrl(url);
                return true;
            
    );

    String postData = FIELD_NAME_LOGIN + "=" + LOGIN +
            "&" + FIELD_NAME_PASSWD + "=" + PASSWD;

    // this line logs you in and you stay logged in
    // I suppose it works this way because in this case WebView handles cookies itself
    webv.postUrl(URL, EncodingUtils.getBytes(postData, "utf-8"));

【讨论】:

以上是关于Android:使用 DefaultHttpClient 登录网站并保留会话/cookie的主要内容,如果未能解决你的问题,请参考以下文章

Android之SharedPreferences使用

想要使用cordova/android禁用android的HardBack按钮

如何在Mac中使用Android SDK

何时使用“?android”或“@android”?

Android 安装包优化Android 中使用 SVG 图片 ( 使用 appcompat 支持库兼容 5.0 以下版本的 Android 系统使用矢量图 )

Android Handler使用