Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: log into website and preserve session/cookie using DefaultHttpClient

I've been through different tutorials and this website, but couldn't find a proper solution. On the other hand, I've seen apps logging into websites and requesting further information, so I'm sure there's a way to get this working, but maybe my approach is all wrong.

Here's what I'm trying to do: I want to log into a website that needs user authentication and then read and parse websites that are only accessible if the user is logged in. The problem: after POSTing the credentials to the website, I receive a cookie which doesn't seem to be preserved in my HttpClient, even though the docs suggest that exactly that should happen.

Here's some of my code:

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

When I output the contents of "cookies", everything seems fine (I receive a session):

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

As I understood, the cookie/session will be preserved and used in my HttpClient as long as I don't close it.

When reading the next page (which is restricted), using this code:

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

If I output the response of the GET-request (using response.getStatusLine()) I get a "200 OK" message, but parsing the site that is returned shows, that the login is lost (I only retrieve a login form).

Any help is appreciated.

like image 750
Select0r Avatar asked Aug 08 '10 13:08

Select0r


3 Answers

In an application that I have to login to. First i have to run a GET followed by a POST and then the GET again. The First get will instantiate a Jsession Id for my connection. The POST will authenticate my ID and then the original get GET will return the real content.

The code below is for an app running in 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;   
}
like image 165
technoSpino Avatar answered Nov 15 '22 20:11

technoSpino


Assuming your httpclient object is the same in both cases, and assuming the RESTRICTED_URL is in the same domain as the LOGIN_URL, then I would think what you have should work.

You might wish to use Wireshark or a proxy or something to examine the HTTP requests you are making, to see if the cookie is actually being attached to the request. It may be that the cookie is being attached, in which case there is something else wrong that is causing your second request to fail.

like image 43
CommonsWare Avatar answered Nov 15 '22 19:11

CommonsWare


You have to make DefaultHttpClient httpclient with singleton pattern so sessioncookie that you have still hold session from login.

This is the Mainactivity class :

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

and this is the RequestPage class:

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;
}
like image 38
dreamfighter Avatar answered Nov 15 '22 19:11

dreamfighter