Использование файлов cookie с Android-библиотекой volley
Кто-нибудь знает, как связать cookie сеанса с запросом, используя библиотеку com.android.volley?
Когда я вхожу на веб-сайт, он дает мне сеансовый файл cookie. Браузер отправит этот файл cookie с любым последующим запросом. Волейбол, похоже, не делает этого, по крайней мере, не автоматически.
Спасибо.
Ответы
Ответ 1
Вмиринов прав!
Вот как я решил проблему:
Класс запроса:
public class StringRequest extends com.android.volley.toolbox.StringRequest {
private final Map<String, String> _params;
/**
* @param method
* @param url
* @param params
* A {@link HashMap} to post with the request. Null is allowed
* and indicates no parameters will be posted along with request.
* @param listener
* @param errorListener
*/
public StringRequest(int method, String url, Map<String, String> params, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, listener, errorListener);
_params = params;
}
@Override
protected Map<String, String> getParams() {
return _params;
}
/* (non-Javadoc)
* @see com.android.volley.toolbox.StringRequest#parseNetworkResponse(com.android.volley.NetworkResponse)
*/
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
// since we don't know which of the two underlying network vehicles
// will Volley use, we have to handle and store session cookies manually
MyApp.get().checkSessionCookie(response.headers);
return super.parseNetworkResponse(response);
}
/* (non-Javadoc)
* @see com.android.volley.Request#getHeaders()
*/
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = super.getHeaders();
if (headers == null
|| headers.equals(Collections.emptyMap())) {
headers = new HashMap<String, String>();
}
MyApp.get().addSessionCookie(headers);
return headers;
}
}
и MyApp:
public class MyApp extends Application {
private static final String SET_COOKIE_KEY = "Set-Cookie";
private static final String COOKIE_KEY = "Cookie";
private static final String SESSION_COOKIE = "sessionid";
private static MyApp _instance;
private RequestQueue _requestQueue;
private SharedPreferences _preferences;
public static MyApp get() {
return _instance;
}
@Override
public void onCreate() {
super.onCreate();
_instance = this;
_preferences = PreferenceManager.getDefaultSharedPreferences(this);
_requestQueue = Volley.newRequestQueue(this);
}
public RequestQueue getRequestQueue() {
return _requestQueue;
}
/**
* Checks the response headers for session cookie and saves it
* if it finds it.
* @param headers Response Headers.
*/
public final void checkSessionCookie(Map<String, String> headers) {
if (headers.containsKey(SET_COOKIE_KEY)
&& headers.get(SET_COOKIE_KEY).startsWith(SESSION_COOKIE)) {
String cookie = headers.get(SET_COOKIE_KEY);
if (cookie.length() > 0) {
String[] splitCookie = cookie.split(";");
String[] splitSessionId = splitCookie[0].split("=");
cookie = splitSessionId[1];
Editor prefEditor = _preferences.edit();
prefEditor.putString(SESSION_COOKIE, cookie);
prefEditor.commit();
}
}
}
/**
* Adds session cookie to headers if exists.
* @param headers
*/
public final void addSessionCookie(Map<String, String> headers) {
String sessionId = _preferences.getString(SESSION_COOKIE, "");
if (sessionId.length() > 0) {
StringBuilder builder = new StringBuilder();
builder.append(SESSION_COOKIE);
builder.append("=");
builder.append(sessionId);
if (headers.containsKey(COOKIE_KEY)) {
builder.append("; ");
builder.append(headers.get(COOKIE_KEY));
}
headers.put(COOKIE_KEY, builder.toString());
}
}
}
Ответ 2
Volley фактически не выполняет запросы HTTP непосредственно и, таким образом, не управляет Cookies напрямую. Вместо этого вместо этого используется экземпляр HttpStack. Существуют две основные реализации:
- HurlStack: использует HttpUrlConnection под капотом
- HttpClientStack: использует Apache HttpClient под капотом
Управление файлами cookie является обязанностью тех HttpStacks. И каждый из них обрабатывает Cookies по-разному.
Если вам необходимо поддерживать < 2.3, то вы должны использовать HttpClientStack:
Настройте экземпляр HttpClient и передайте его волейболу для использования под капотом:
// If you need to directly manipulate cookies later on, hold onto this client
// object as it gives you access to the Cookie Store
DefaultHttpClient httpclient = new DefaultHttpClient();
CookieStore cookieStore = new BasicCookieStore();
httpclient.setCookieStore( cookieStore );
HttpStack httpStack = new HttpClientStack( httpclient );
RequestQueue requestQueue = Volley.newRequestQueue( context, httpStack );
Преимущество этой команды против вставки файлов cookie вручную в заголовки заключается в том, что вы получаете фактическое управление файлами cookie. Cookies в вашем магазине будут правильно отвечать на HTTP-элементы управления, которые истекают или обновляют их.
Я пошел дальше и подклассифицировал BasicCookieStore, чтобы я мог автоматически сохранять файлы cookie на диск.
ОДНАКО! Если вам не нужна поддержка старых версий Android. Просто используйте этот метод:
// CookieStore is just an interface, you can implement it and do things like
// save the cookies to disk or what ever.
CookieStore cookieStore = new MyCookieStore();
CookieManager manager = new CookieManager( cookieStore, CookiePolicy.ACCEPT_ALL );
CookieHandler.setDefault( manager );
// Optionally, you can just use the default CookieManager
CookieManager manager = new CookieManager();
CookieHandler.setDefault( manager );
HttpURLConnection будет запрашивать CookieManager из этого неявно. HttpUrlConnection также более эффективен и немного чище для реализации и работы с IMO.
Ответ 3
Транспортный код HTTP по умолчанию для Volley - HttpUrlConnection
. Если я правильно читаю документацию, вам нужно выбрать автоматическую поддержку cookie сеанса:
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);
См. также Должен ли HttpURLConnection с CookieManager автоматически обрабатывать файлы cookie сеанса?
Ответ 4
Решение @Rastio не работает, если есть несколько заголовков Set-Cookie. Я завернул файл cookie cookie cookie по умолчанию и перед добавлением файла cookie я сохранил его в SharedPreferences с помощью Gson для сериализации файла cookie.
Это пример обертки магазина cookie:
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.google.gson.Gson;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.util.List;
/**
* Class that implements CookieStore interface. This class saves to SharedPreferences the session
* cookie.
*
* Created by lukas.
*/
public class PersistentCookieStore implements CookieStore {
private CookieStore mStore;
private Context mContext;
private Gson mGson;
public PersistentCookieStore(Context context) {
// prevent context leaking by getting the application context
mContext = context.getApplicationContext();
mGson = new Gson();
// get the default in memory store and if there is a cookie stored in shared preferences,
// we added it to the cookie store
mStore = new CookieManager().getCookieStore();
String jsonSessionCookie = Prefs.getJsonSessionCookie(mContext);
if (!jsonSessionCookie.equals(Prefs.DEFAULT_STRING)) {
HttpCookie cookie = mGson.fromJson(jsonSessionCookie, HttpCookie.class);
mStore.add(URI.create(cookie.getDomain()), cookie);
}
}
@Override
public void add(URI uri, HttpCookie cookie) {
if (cookie.getName().equals("sessionid")) {
// if the cookie that the cookie store attempt to add is a session cookie,
// we remove the older cookie and save the new one in shared preferences
remove(URI.create(cookie.getDomain()), cookie);
Prefs.saveJsonSessionCookie(mContext, mGson.toJson(cookie));
}
mStore.add(URI.create(cookie.getDomain()), cookie);
}
@Override
public List<HttpCookie> get(URI uri) {
return mStore.get(uri);
}
@Override
public List<HttpCookie> getCookies() {
return mStore.getCookies();
}
@Override
public List<URI> getURIs() {
return mStore.getURIs();
}
@Override
public boolean remove(URI uri, HttpCookie cookie) {
return mStore.remove(uri, cookie);
}
@Override
public boolean removeAll() {
return mStore.removeAll();
}
}
Затем, чтобы использовать хранилище cookie, только что установленное в CookieManager, и что это!
CookieManager cookieManager = new CookieManager(new PersistentCookieStore(mContext),
CookiePolicy.ACCEPT_ORIGINAL_SERVER);
CookieHandler.setDefault(cookieManager);
Ответ 5
Ребята пробуют это в методе onCreate
вашего AppController.java
CookieHandler.setDefault(new CookieManager());
Надеюсь, это сэкономит время разработчиков. Я потратил четыре часа на отладку и поиск подходящего решения.
Ответ 6
Я знаю сообщение и немного старый, но мы рассмотрели эту недавнюю проблему, нам нужно поделиться сеансом зарегистрированного пользователя между серверами, а серверное решение начинает требовать, чтобы значение предоставлялось клиентской стороной, через cookie.
Одно из найденных нами решений заключалось в том, чтобы добавить параметр к объекту RequestQueue
, фрагмент кода в методе getRequestQueue
до создания экземпляра RequestQueue
, найденного по ссылке ниже, и решить проблему, не уверен, как это сделать, но она начала работать.
Посетите http://woxiangbo.iteye.com/blog/1769122
public class App extends Application {
public static final String TAG = App.class.getSimpleName();
private static App mInstance;
public static synchronized App getInstance() {
return App.mInstance;
}
private RequestQueue mRequestQueue;
public <T> void addToRequestQueue( final Request<T> req ) {
req.setTag( App.TAG );
this.getRequestQueue().add( req );
}
public <T> void addToRequestQueue( final Request<T> req, final String tag ) {
req.setTag( TextUtils.isEmpty( tag ) ? App.TAG : tag );
this.getRequestQueue().add( req );
}
public void cancelPendingRequests( final Object tag ) {
if ( this.mRequestQueue != null ) {
this.mRequestQueue.cancelAll( tag );
}
}
public RequestQueue getRequestQueue() {
if ( this.mRequestQueue == null ) {
DefaultHttpClient mDefaultHttpClient = new DefaultHttpClient();
final ClientConnectionManager mClientConnectionManager = mDefaultHttpClient.getConnectionManager();
final HttpParams mHttpParams = mDefaultHttpClient.getParams();
final ThreadSafeClientConnManager mThreadSafeClientConnManager = new ThreadSafeClientConnManager( mHttpParams, mClientConnectionManager.getSchemeRegistry() );
mDefaultHttpClient = new DefaultHttpClient( mThreadSafeClientConnManager, mHttpParams );
final HttpStack httpStack = new HttpClientStack( mDefaultHttpClient );
this.mRequestQueue = Volley.newRequestQueue( this.getApplicationContext(), httpStack );
}
return this.mRequestQueue;
}
@Override
public void onCreate() {
super.onCreate();
App.mInstance = this;
}
}
//установить значение токена
ObjectRequest.setHeader( "Cookie", "JSESSIONID=" + tokenValueHere );
Ответ 7
Используйте этот метод для использования волейбола с файлами cookie для:
- Используйте только очень хорошо протестированный код, лицензированный в соответствии с лицензией Apache 2.
- Сделайте столько запросов, сколько захотите, в то же время
- Убедитесь, что файлы cookie сохраняются на устройстве.
- Не нужно изобретать колесо
Мой сервер использует файлы cookie для аутентификации и, очевидно, я хочу, чтобы файлы cookie сохранялись на устройстве. Поэтому я решил использовать PersistentCookieStore и SerializableCookie классы из Асинхронный клиент Http для Android.
Во-первых, для включения параллельных запросов необходим порт Apache HttpClient v4.3 для Android - тот, который поставляется с системой, устарел. Подробнее здесь. Я использую Gradle, так вот как я его импортировал:
dependencies {
compile group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.3'
}
Функция для получения RequestQueue (в моем классе, который расширяет приложение):
private RequestQueue mRequestQueue;
private CloseableHttpClient httpClient;
...
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
httpClient = HttpClients.custom()
.setConnectionManager(new PoolingHttpClientConnectionManager())
.setDefaultCookieStore(new PersistentCookieStore(getApplicationContext()))
.build();
mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new HttpClientStack(httpClient));
}
return mRequestQueue;
}
Вот как я помещаю в очередь запрос
public <T> void addToRequestQueue(Request<T> req, String tag) {
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
Что это!
Ответ 8
gingerbread+ Версии Android:
существует еще один простой способ сохранить сеанс cookie, и это необходимо добавить эту строку в класс, расширенный классом APPLICATION:
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
Ответ 9
Если вы уже начали внедрять приложение с помощью библиотеки Loopj, вы заметите, что вы не можете использовать новый экземпляр HttpClient в Volley.newRequestQUeue(), потому что вы получите различные ошибки о том, что вы не закрываете предыдущее соединение и т.д.
Ошибки вроде:
java.lang.IllegalStateException: No wrapped connection
Invalid use of SingleClientConnManager: connection still allocated.
Теперь иногда требуется время, чтобы реорганизовать все ваши старые вызовы API и переписать их с помощью волейбола, но вы можете одновременно использовать волейбол и loopj и делиться кулинарным способом между этими двумя, пока вы не напишете все в залпе (используйте волейбол вместо loopj, это намного лучше:)).
Вот как вы можете делиться HttpClient и CookieStore с loopj с помощью volley.
// For example you initialize loopj first
private static AsyncHttpClient client = new AsyncHttpClient();
sCookieStore = new PersistentCookieStore(getSomeContextHere());
client.setTimeout(DEFAULT_TIMEOUT);
client.setMaxConnections(12);
client.setCookieStore(sCookieStore);
client.setThreadPool(((ThreadPoolExecutor) Executors.newCachedThreadPool()));
public static RequestQueue getRequestQueue(){
if(mRequestQueue == null){
HttpClient httpclient = KkstrRestClient.getClient().getHttpClient();
((AbstractHttpClient) httpclient).setCookieStore( ApplicationController.getCookieStore() );
HttpStack httpStack = new HttpClientStack(httpclient);
mRequestQueue = Volley.newRequestQueue(getContext(), httpStack);
}
return mRequestQueue;
}
Это случилось со мной, мы начали использовать loopj. После 50 000 строк кода и обнаружения, что loopj не всегда работает так, как ожидалось, мы решили переключиться на Volley.
Ответ 10
Ответ @CommonsWare - тот, который я бы использовал. Тем не менее, похоже, что KitKat имеет некоторые ошибки, когда это было сделано (когда вы создаете CookieManager
с пользовательским CookieStore
, который вам нужен, если вам нужны постоянные Cookies).
Учитывая тот факт, что независимо от реализации используемого CookieStore
, Volley выбрал бы NullpointerException
, мне пришлось создать свой собственный CookieHandler
... использовать его, если вы сочтете это полезным.
public class MyCookieHandler extends CookieHandler {
private static final String VERSION_ZERO_HEADER = "Set-cookie";
private static final String VERSION_ONE_HEADER = "Set-cookie2";
private static final String COOKIE_HEADER = "Cookie";
private static final String COOKIE_FILE = "Cookies";
private Map<String, Map<String, HttpCookie>> urisMap;
private Context context;
public MyCookieHandler(Context context) {
this.context = context;
loadCookies();
}
@SuppressWarnings("unchecked")
private void loadCookies() {
File file = context.getFileStreamPath(COOKIE_FILE);
if (file.exists())
try {
FileInputStream fis = context.openFileInput(COOKIE_FILE);
BufferedReader br = new BufferedReader(new InputStreamReader(
fis));
String line = br.readLine();
StringBuilder sb = new StringBuilder();
while (line != null) {
sb.append(line);
line = br.readLine();
}
Log.d("MyCookieHandler.loadCookies", sb.toString());
JSONObject jsonuris = new JSONObject(sb.toString());
urisMap = new HashMap<String, Map<String, HttpCookie>>();
Iterator<String> jsonurisiter = jsonuris.keys();
while (jsonurisiter.hasNext()) {
String prop = jsonurisiter.next();
HashMap<String, HttpCookie> cookiesMap = new HashMap<String, HttpCookie>();
JSONObject jsoncookies = jsonuris.getJSONObject(prop);
Iterator<String> jsoncookiesiter = jsoncookies.keys();
while (jsoncookiesiter.hasNext()) {
String pprop = jsoncookiesiter.next();
cookiesMap.put(pprop,
jsonToCookie(jsoncookies.getJSONObject(pprop)));
}
urisMap.put(prop, cookiesMap);
}
} catch (Exception e) {
e.printStackTrace();
}
else {
urisMap = new HashMap<String, Map<String, HttpCookie>>();
}
}
@Override
public Map<String, List<String>> get(URI arg0,
Map<String, List<String>> arg1) throws IOException {
Log.d("MyCookieHandler.get",
"getting Cookies for domain: " + arg0.getHost());
Map<String, HttpCookie> cookies = urisMap.get(arg0.getHost());
if (cookies != null)
for (Entry<String, HttpCookie> cookie : cookies.entrySet()) {
if (cookie.getValue().hasExpired()) {
cookies.remove(cookie.getKey());
}
}
if (cookies == null || cookies.isEmpty()) {
Log.d("MyCookieHandler.get", "======");
return Collections.emptyMap();
}
Log.d("MyCookieHandler.get",
"Cookie : " + TextUtils.join("; ", cookies.values()));
Log.d("MyCookieHandler.get", "======");
return Collections.singletonMap(COOKIE_HEADER, Collections
.singletonList(TextUtils.join("; ", cookies.values())));
}
@Override
public void put(URI uri, Map<String, List<String>> arg1) throws IOException {
Map<String, HttpCookie> cookies = parseCookies(arg1);
Log.d("MyCookieHandler.put",
"saving Cookies for domain: " + uri.getHost());
addCookies(uri, cookies);
Log.d("MyCookieHandler.put",
"Cookie : " + TextUtils.join("; ", cookies.values()));
Log.d("MyCookieHandler.put", "======");
}
private void addCookies(URI uri, Map<String, HttpCookie> cookies) {
if (!cookies.isEmpty()) {
if (urisMap.get(uri.getHost()) == null) {
urisMap.put(uri.getHost(), cookies);
} else {
urisMap.get(uri.getHost()).putAll(cookies);
}
saveCookies();
}
}
private void saveCookies() {
try {
FileOutputStream fos = context.openFileOutput(COOKIE_FILE,
Context.MODE_PRIVATE);
JSONObject jsonuris = new JSONObject();
for (Entry<String, Map<String, HttpCookie>> uris : urisMap
.entrySet()) {
JSONObject jsoncookies = new JSONObject();
for (Entry<String, HttpCookie> savedCookies : uris.getValue()
.entrySet()) {
jsoncookies.put(savedCookies.getKey(),
cookieToJson(savedCookies.getValue()));
}
jsonuris.put(uris.getKey(), jsoncookies);
}
fos.write(jsonuris.toString().getBytes());
fos.close();
Log.d("MyCookieHandler.addCookies", jsonuris.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
private static JSONObject cookieToJson(HttpCookie cookie) {
JSONObject jsoncookie = new JSONObject();
try {
jsoncookie.put("discard", cookie.getDiscard());
jsoncookie.put("maxAge", cookie.getMaxAge());
jsoncookie.put("secure", cookie.getSecure());
jsoncookie.put("version", cookie.getVersion());
jsoncookie.put("comment", cookie.getComment());
jsoncookie.put("commentURL", cookie.getCommentURL());
jsoncookie.put("domain", cookie.getDomain());
jsoncookie.put("name", cookie.getName());
jsoncookie.put("path", cookie.getPath());
jsoncookie.put("portlist", cookie.getPortlist());
jsoncookie.put("value", cookie.getValue());
} catch (JSONException e) {
e.printStackTrace();
}
return jsoncookie;
}
private static HttpCookie jsonToCookie(JSONObject jsonObject) {
HttpCookie httpCookie;
try {
httpCookie = new HttpCookie(jsonObject.getString("name"),
jsonObject.getString("value"));
if (jsonObject.has("comment"))
httpCookie.setComment(jsonObject.getString("comment"));
if (jsonObject.has("commentURL"))
httpCookie.setCommentURL(jsonObject.getString("commentURL"));
if (jsonObject.has("discard"))
httpCookie.setDiscard(jsonObject.getBoolean("discard"));
if (jsonObject.has("domain"))
httpCookie.setDomain(jsonObject.getString("domain"));
if (jsonObject.has("maxAge"))
httpCookie.setMaxAge(jsonObject.getLong("maxAge"));
if (jsonObject.has("path"))
httpCookie.setPath(jsonObject.getString("path"));
if (jsonObject.has("portlist"))
httpCookie.setPortlist(jsonObject.getString("portlist"));
if (jsonObject.has("secure"))
httpCookie.setSecure(jsonObject.getBoolean("secure"));
if (jsonObject.has("version"))
httpCookie.setVersion(jsonObject.getInt("version"));
return httpCookie;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
private Map<String, HttpCookie> parseCookies(Map<String, List<String>> map) {
Map<String, HttpCookie> response = new HashMap<String, HttpCookie>();
for (Entry<String, List<String>> e : map.entrySet()) {
String key = e.getKey();
if (key != null
&& (key.equalsIgnoreCase(VERSION_ONE_HEADER) || key
.equalsIgnoreCase(VERSION_ZERO_HEADER))) {
for (String cookie : e.getValue()) {
try {
for (HttpCookie htpc : HttpCookie.parse(cookie)) {
response.put(htpc.getName(), htpc);
}
} catch (Exception e1) {
Log.e("MyCookieHandler.parseCookies",
"Error parsing cookies", e1);
}
}
}
}
return response;
}
}
Этот ответ не был тщательно протестирован. Я использовал JSON для сериализации Cookies, потому что хорошо, что этот класс не реализует Serializable
и он окончательный.
Ответ 11
В моем проекте CookieManager
разрешен как CookieManager
android.webkit.CookieManager
. Я должен установить обработчик следующим образом, чтобы заставить Автозагрузку Volley обрабатывать файлы cookie.
CookieManager cookieManager = новый java.net.CookieManager(); CookieHandler.setDefault(cookieManager);
Ответ 12
Вот один из самых простых рабочих примеров. Мне не нравится REST, что не успокаивает. Но я думаю, все идет как опыт обучения.
Самое главное отметить в следующем примере: curlget.php не имеет session_start. Какой язык вы используете для написания этой части, зависит от вас. В следующем примере это может быть что угодно: от java до ржавчины. Он также может найти где угодно.
login.php
Код PHP:
<?php
session_start();
//we store any POST/GET request to session
$_SESSION['un'] = $_REQUEST['username'];
userinfo.php
PHP Code:
<?php
session_start();
echo json_encode($_SESSION['un']);
curlget.php
Код PHP:
<?php
$ch = curl_init();
//we do login and get the session_id in from it responce headers
curl_setopt($ch, CURLOPT_URL,"http://localhost/login.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,"username=test");
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec ($ch);
//we get the cookie from response header with the hardest possible way
//most platforms have tools for these
preg_match_all('/^Set-Cookie:\s*([^\r\n]*)/mi', $result, $ms);
$cookies = array();
foreach ($ms[1] as $m) {
list($name, $value) = explode('=', $m, 2);
$cookies[$name] = $value;
}
$parts = explode(";",$cookies['PHPSESSID']);
//The SessionId finally
$SID = $parts[0];
curl_close ($ch);
//Запрос GET с использованием SID из предыдущего вызова