Использование файлов cookie в действиях при использовании HttpClient - PullRequest
26 голосов
/ 27 апреля 2011

Я пытаюсь простое приложение для чтения в HTML веб-сайта и трансформирую его для создания легко читаемого пользовательского интерфейса в Android (это большой опыт в изучении Android, а не в создании пригодного для использования приложения).Проблема, которую я имею, состоит в том, чтобы сохранить сессию пользователей через Действия и затем использовать сессию в HttpClient однажды вызванном.

Я хотел бы сделать это "Правильно", рекомендуемый подход, похоже, использовать CookieManager.Однако у меня были проблемы с этим - я не могу найти «правильный» способ взять Cookie из CookieManager и использовать его в более позднем экземпляре HttpClient в отдельной деятельности.

При использовании CookieManager я могу сохранить Cookie, и тогда Cookie входит в область действия других действий (см. Фрагмент кода 2).Я не нашел, как использовать это позже (см. Фрагмент кода 3) при запросе страницы.

Достаточно разговоров, вот код.Сначала мое действие при входе в систему и хранение файлов cookie:

private OnClickListener loginActionListener = new OnClickListener() 
{
    public void onClick(View v) 
    {
        EditText usernameTextView = (EditText) findViewById(R.id.Username);
        EditText passwordTextView = (EditText) findViewById(R.id.Password);
        String username = usernameTextView.getText().toString();
        String password = passwordTextView.getText().toString();

        try {
            HttpPost postMethod = new HttpPost(URI);                
            HttpParams params   = new BasicHttpParams();

            params.setParameter("mode", "login");
            params.setParameter("autologin", true);
            params.setParameter("username", username);
            params.setParameter("password", password);
            postMethod.setParams(params);

            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpResponse response        = httpClient.execute(postMethod);
            List<Cookie> cookies = httpClient.getCookieStore().getCookies();

            if(cookies != null)
            {
                for(Cookie cookie : cookies)
                {
                    String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain();                        
                    CookieManager.getInstance().setCookie(cookie.getDomain(), cookieString);  
                }
            }
            CookieSyncManager.getInstance().sync();

            Intent intent = new Intent(v.getContext(), IndexAction.class);
            startActivity(intent);
    } catch (Exception e) {...}
}

Ниже описана активность при запуске, которая решает сделать вход в систему пользователем или перейти к индексу.Из этого кода видно, что файл cookie находится в области видимости и может быть прочитан:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    CookieSyncManager.createInstance(this);

    if(CookieManager.getInstance().getCookie(URI) == null)
    {
        Intent intent = new Intent(this, LoginAction.class);
        startActivity(intent);
    }
    else
    {
        Intent intent = new Intent(this, IndexAction.class);
        startActivity(intent);
    }
}

Но из моего кода для чтения страницы индекса я надеюсь, что вы можете подсказать, чего мне не хватает:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    CookieSyncManager.createInstance(this);

    try
    {
            HttpGet getMethod = new HttpGet(URI_INDEX);  

            HttpParams params   = new BasicHttpParams();                        
            HttpConnectionParams.setConnectionTimeout(params, 30000);
            HttpConnectionParams.setSoTimeout(params, 30000);

            // This code results in a ClassCastException, I'm assuming i've found a red herring with this solution.
            // HttpContext localContext = new BasicHttpContext();    
            // localContext.setAttribute(ClientContext.COOKIE_STORE, CookieManager.getInstance().getCookie(URI));

            DefaultHttpClient httpClient = new DefaultHttpClient(params);
            HttpResponse response        = httpClient.execute(getMethod);

            if(response.getStatusLine().getStatusCode() > 299 && response.getStatusLine().getStatusCode() < 400)
            {
                // Not logged in doesn't give a redirect response. Very annoying.
            }

            final char[] buffer = new char[0x10000];
            StringBuilder out = new StringBuilder();
            Reader in = new InputStreamReader(response.getEntity().getContent(), "UTF-8");
            int read = 0;
            while (read>=0)
            {
              read = in.read(buffer, 0, buffer.length);
              if (read>0) {
                out.append(buffer, 0, read);
              }
            }

            String returnString = out.toString();
    } catch (ClientProtocolException e) {...}
}

HttpClient на execute(getMethod) не использует Cookie (дважды проверил это при отладке) для возврата страницы.Было бы здорово, если бы кто-то мог заполнить эту дыру в моих знаниях.

Заранее спасибо.

РЕДАКТИРОВАТЬ

Когда код с комментариями добавляется обратно в(с изменением метода httpClient.execute(getMethod) на httpClient.execute(getMethod, localContext)) создается эта трассировка трека - предположительно потому, что я заполняю атрибут ClientContext.COOKIE_STORE Cookie String, а не CookieStore:

*org.apache.http.client.protocol.RequestAddCookies.process(RequestAddCookies.java:88), org.apache.http.protocol.BasicHttpProcessor.process(BasicHttpProcessor.java:290), org.apache.http.protocol.HttpRequestExecutor.preProcess(HttpRequestExecutor.java:160), org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:401)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555), org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487), 
com.testapp.site.name.IndexAction.onCreate(IndexAction.java:47), 
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047), 
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611), 
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663), 
android.app.ActivityThread.access$1500(ActivityThread.java:117), 
android.app.ActivityThread$H.handleMessage(ActivityThread.java:931), 
android.os.Handler.dispatchMessage(Handler.java:99), 
android.os.Looper.loop(Looper.java:123), 
android.app.ActivityThread.main(ActivityThread.java:3683), 
java.lang.reflect.Method.invokeNative(Native Method), 
java.lang.reflect.Method.invoke(Method.java:507), 
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839), 
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597), 
dalvik.system.NativeStart.main(Native Method)*

Ответы [ 6 ]

23 голосов
/ 28 апреля 2011

CookieManager используется внутренним HTTP-клиентом Java.Это не имеет ничего общего с Apache HttpClient.

В своем коде вы всегда создаете для каждого запроса новый экземпляр HttpClient и, следовательно, новый экземпляр CookieStore, который, очевидно, собирает мусор вместе со всеми файлами cookie, сохраненными в этом экземпляре HttpClientвыходит за рамки.

Вы должны либо

(1) Повторно использовать один и тот же экземпляр HttpClient для всех логически связанных HTTP-запросов и делиться им между всеми логически связанными потоками (которыерекомендуется использовать Apache HttpClient)

(2) или, по крайней мере, совместно использовать один и тот же экземпляр CookieStore между логически связанными потоками

(3) или, если вы настаиваете на использовании CookieManager для хранения всех ваших файлов cookie, создайте пользовательскую реализацию CookieStore с поддержкой CookieManager

10 голосов
/ 13 мая 2011

(Как и обещал решение этой проблемы. Мне все еще не нравится это, и я чувствую, что упускаю «правильный» способ сделать это, но это работает.)

Вы можете использовать CookieManager, чтобы зарегистрировать свои куки (и, следовательно, сделать эти куки доступными между приложениями) с помощью следующего кода:

Сохранение файлов cookie в CookieManager:

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

if(cookies != null)
{
    for(Cookie cookie : cookies)
    {
        String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain();                        
        CookieManager.getInstance().setCookie(cookie.getDomain(), cookieString);  
    }
}
CookieSyncManager.getInstance().sync();

Проверка файлов cookie на указанном домене: if(CookieManager.getInstance().getCookie(URI_FOR_DOMAIN)

Чтобы восстановить значения для HttpClient:

DefaultHttpClient httpClient = new DefaultHttpClient(params);
String[] keyValueSets = CookieManager.getInstance().getCookie(URI_FOR_DOMAIN).split(";");
for(String cookie : keyValueSets)
{
    String[] keyValue = cookie.split("=");
    String key = keyValue[0];
    String value = "";
    if(keyValue.length>1) value = keyValue[1];
    httpClient.getCookieStore().addCookie(new BasicClientCookie(key, value));
}
4 голосов
/ 07 августа 2013

В моем сервере приложений хочет использовать тот же сеанс с теми же куки ... После нескольких часов "поиска в Google" и мучительной головной боли я просто сохранил куки в SharedPreference или просто вставил какой-то объект и снова установил DefaultHttpClient с теми же куки ... onDestroy просто удалите SharedPreferences ... вот и все:

  1. Сначала скопируйте класс SerializableCookie в ваш пакет: SerializableCookie

Смотрите следующий пример:

public class NodeServerTask extends AsyncTask<Void, Void, String> {
private DefaultHttpClient client;


protected String doInBackground(Void... params) {
  HttpPost httpPost = new HttpPost(url);
    List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
    urlParameters.add(nameValuePair);
    try {
        httpPost.setEntity(new UrlEncodedFormEntity(urlParameters));
        List<Cookie> cookies = loadSharedPreferencesCookie();
        if (cookies!=null){
            CookieStore cookieStore = new BasicCookieStore();
            for (int i=0; i<cookies.size(); i++)
                cookieStore.addCookie(cookies.get(i));
            client.setCookieStore(cookieStore);
        }
        HttpResponse response = client.execute(httpPost);
        cookies = client.getCookieStore().getCookies();

        saveSharedPreferencesCookies(cookies);

// два способа сохранить и загрузить куки ...

 private void saveSharedPreferencesCookies(List<Cookie> cookies) {
    SerializableCookie[] serializableCookies = new SerializableCookie[cookies.size()];
    for (int i=0;i<cookies.size();i++){
        SerializableCookie serializableCookie = new SerializableCookie(cookies.get(i));
        serializableCookies[i] = serializableCookie;
    }
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    SharedPreferences.Editor editor = preferences.edit();
    ObjectOutputStream objectOutput;
    ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
    try {
        objectOutput = new ObjectOutputStream(arrayOutputStream);


        objectOutput.writeObject(serializableCookies);
        byte[] data = arrayOutputStream.toByteArray();
        objectOutput.close();
        arrayOutputStream.close();

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Base64OutputStream b64 = new Base64OutputStream(out, Base64.DEFAULT);
        b64.write(data);
        b64.close();
        out.close();

        editor.putString("cookies", new String(out.toByteArray()));
        editor.apply();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private List<Cookie> loadSharedPreferencesCookie() {
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
    byte[] bytes = preferences.getString("cookies", "{}").getBytes();
    if (bytes.length == 0 || bytes.length==2)
        return null;
    ByteArrayInputStream byteArray = new ByteArrayInputStream(bytes);
    Base64InputStream base64InputStream = new Base64InputStream(byteArray, Base64.DEFAULT);
    ObjectInputStream in;
    List<Cookie> cookies = new ArrayList<Cookie>();
    SerializableCookie[] serializableCookies;
    try {
        in = new ObjectInputStream(base64InputStream);
        serializableCookies = (SerializableCookie[]) in.readObject();
        for (int i=0;i<serializableCookies.length; i++){
            Cookie cookie = serializableCookies[i].getCookie();
            cookies.add(cookie);
        }
        return cookies;
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return null;
}

Google Luck

1 голос
/ 11 августа 2015

У меня была такая же проблема. Поскольку я использую Volley, а не HTTPClient напрямую, Singleton for HTTPClient не кажется правильным решением. Но, используя ту же идею, я просто сохранил CookieManager в моем приложении-синглтоне. Поэтому, если приложение является моим приложением-одиночкой, в onCreate () каждого действия я добавляю следующее:

    if (app.cookieManager == null) {
        app.cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
    }
    CookieHandler.setDefault(app.cookieManager);

Чтобы сделать это еще лучше, все мои действия являются подклассами MyAppActivity, поэтому все, что мне нужно сделать, это поместить его в onCreate для MyAppActivity, а затем все мои действия наследуют эту функциональность. Теперь все мои действия используют один и тот же менеджер файлов cookie, и мой сеанс веб-службы является общим для всех действий. Просто и отлично работает.

0 голосов
/ 16 июля 2013

Полагаю, одно из лучших применений - применить шаблон Singleton к вашему HTTPClient, чтобы у вас был только один экземпляр!

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;

public class myHttpClient {
    private static HttpClient mClient;
    private myHttpClient() {};
    public static HttpClient getInstance() {
        if(mClient==null)
            mClient = new DefaultHttpClient();
        return mClient;
    }
}
0 голосов
/ 25 января 2013

Хорошим решением для хранения файлов cookie является следование шаблону ThreadLocal, чтобы избежать случайного хранения файлов cookie.

Это поможет сохранить один-единственный CookieStore.Для более подробного объяснения этого паттерна вы можете перейти по этой полезной ссылке.

http://veerasundar.com/blog/2010/11/java-thread-local-how-to-use-and-code-sample/

Cheers

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...