Spring Android: использование RestTemplate с https и куки - PullRequest
11 голосов
/ 18 августа 2011

Мне нужно использовать куки-файлы при подключении через https из собственного приложения для Android.Я использую RestTemplate.

Проверка других потоков (например, Настройка файлов cookie безопасности с помощью RestTemplate ) Я смог обработать файлы cookie в подключении http:

restTemplate.setRequestFactory(new YourClientHttpRequestFactory());

гдеYourClientHttpRequestFactory extends SimpleClientHttpRequestFactory

это отлично работает на http, но не на https.

С другой стороны, мне удалось решить проблему https в Android, доверяя SSL-сертификату:

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpUtils.getNewHttpClient()));

где HttpUtils описан здесь: http://www.makeurownrules.com/secure-rest-web-service-mobile-application-android.html

Моя проблема в том, что мне нужно использовать одну реализацию ClientHttpRequestFactory.Поэтому у меня есть 3 варианта:

1) найти способ обработки https с помощью SimpleClientHttpRequestFactory

2) найти способ обработки файлов cookie с использованием HttpComponentsClientHttpRequestFactory

3) использовать другой подход

1 Ответ

8 голосов
/ 13 января 2012

У меня была такая же проблема. Вот мое решение:

Сначала я обработал SSL так же, как вы (я использовал метод Боба Ли).

Печенье это отдельная история. В прошлом я обрабатывал куки-файлы без RestTemplate (то есть просто используя непосредственно класс HttpClient в Apache), передавая экземпляр HttpContext в метод execute HttpClient. Давайте сделаем шаг назад ...

HttpClient имеет несколько перегруженных execute методов, один из которых:

execute(HttpUriRequest request, HttpContext context)

Экземпляр HttpContext может иметь ссылку на CookieStore. При создании экземпляра HttpContext укажите CookieStore (либо новый, либо тот, который вы сохранили из предыдущего запроса):

    private HttpContext createHttpContext() {

    CookieStore cookieStore = (CookieStore) StaticCacheHelper.retrieveObjectFromCache(COOKIE_STORE);
    if (cookieStore == null) {
        Log.d(getClass().getSimpleName(), "Creating new instance of a CookieStore");
        // Create a local instance of cookie store
        cookieStore = new BasicCookieStore();
    } 

    // Create local HTTP context
    HttpContext localContext = new BasicHttpContext();
    // Bind custom cookie store to the local context
    localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
    return localContext;
}

Конечно, вы можете добавить куки в экземпляр CookieStore перед отправкой запроса, если хотите. Теперь, когда вы вызываете метод execute, используйте этот экземпляр HttpContext:

HttpResponse response = httpClient.execute(httpRequester, localContext);

(где httpRequester - это экземпляр HttpPost, HttpGet и т. Д.)

Если вам необходимо повторно отправить файлы cookie при последующих запросах, убедитесь, что вы храните файлы cookie где-либо:

StaticCacheHelper.storeObjectInCache(COOKIE_STORE, localContext.getAttribute(ClientContext.COOKIE_STORE), MAX_MILLISECONDS_TO_LIVE_IN_CACHE);

Класс StaticCacheHelper, который используется в этом коде, является просто пользовательским классом, который может хранить данные в статической карте:

public class StaticCacheHelper {

private static final int TIME_TO_LIVE = 43200000; // 12 hours

private static Map<String, Element> cacheMap = new HashMap<String, Element>();

/**
 * Retrieves an item from the cache. If found, the method compares
 * the object's expiration date to the current time and only returns
 * the object if the expiration date has not passed.
 * 
 * @param cacheKey
 * @return
 */
public static Object retrieveObjectFromCache(String cacheKey) {
    Element e = cacheMap.get(cacheKey);
    Object o = null;
    if (e != null) {
        Date now = new Date();
        if (e.getExpirationDate().after(now)) {
            o = e.getObject();
        } else {
            removeCacheItem(cacheKey);
        }
    }
    return o;
}

/**
 * Stores an object in the cache, wrapped by an Element object.
 * The Element object has an expiration date, which will be set to 
 * now + this class' TIME_TO_LIVE setting.
 * 
 * @param cacheKey
 * @param object
 */
public static void storeObjectInCache(String cacheKey, Object object) {
    Date expirationDate = new Date(System.currentTimeMillis() + TIME_TO_LIVE);
    Element e = new Element(object, expirationDate);
    cacheMap.put(cacheKey, e);
}

/**
 * Stores an object in the cache, wrapped by an Element object.
 * The Element object has an expiration date, which will be set to 
 * now + the timeToLiveInMilliseconds value that is passed into the method.
 * 
 * @param cacheKey
 * @param object
 * @param timeToLiveInMilliseconds
 */
public static void storeObjectInCache(String cacheKey, Object object, int timeToLiveInMilliseconds) {
    Date expirationDate = new Date(System.currentTimeMillis() + timeToLiveInMilliseconds);
    Element e = new Element(object, expirationDate);
    cacheMap.put(cacheKey, e);
}

public static void removeCacheItem(String cacheKey) {
    cacheMap.remove(cacheKey);
}

public static void clearCache() {
    cacheMap.clear();
}

static class Element {

    private Object object;
    private Date expirationDate;

    /**
     * @param object
     * @param key
     * @param expirationDate
     */
    private Element(Object object, Date expirationDate) {
        super();
        this.object = object;
        this.expirationDate = expirationDate;
    }
    /**
     * @return the object
     */
    public Object getObject() {
        return object;
    }
    /**
     * @param object the object to set
     */
    public void setObject(Object object) {
        this.object = object;
    }
    /**
     * @return the expirationDate
     */
    public Date getExpirationDate() {
        return expirationDate;
    }
    /**
     * @param expirationDate the expirationDate to set
     */
    public void setExpirationDate(Date expirationDate) {
        this.expirationDate = expirationDate;
    }
}
}

НО !!!! По состоянию на 01/2012 Android RestTemplate в Spring не дает вам права добавлять HttpContext к выполнению запроса !! Это исправлено в Spring Framework 3.1.0.RELEASE, и это исправление планируется перенести в Spring Android 1.0.0.RC1 .

Итак, когда мы получим Spring Android 1.0.0.RC1, мы сможем добавить контекст, как описано в приведенном выше примере. До тех пор мы должны добавлять / извлекать куки из заголовков запроса / ответа, используя ClientHttpRequestInterceptor.

public class MyClientHttpRequestInterceptor implements
    ClientHttpRequestInterceptor {

private static final String SET_COOKIE = "set-cookie";
private static final String COOKIE = "cookie";
private static final String COOKIE_STORE = "cookieStore";

/* (non-Javadoc)
 * @see org.springframework.http.client.ClientHttpRequestInterceptor#intercept(org.springframework.http.HttpRequest, byte[], org.springframework.http.client.ClientHttpRequestExecution)
 */
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] byteArray,
        ClientHttpRequestExecution execution) throws IOException {

    Log.d(getClass().getSimpleName(), ">>> entering intercept");
    List<String> cookies = request.getHeaders().get(COOKIE);
    // if the header doesn't exist, add any existing, saved cookies
    if (cookies == null) {
        List<String> cookieStore = (List<String>) StaticCacheHelper.retrieveObjectFromCache(COOKIE_STORE);
        // if we have stored cookies, add them to the headers
        if (cookieStore != null) {
            for (String cookie : cookieStore) {
                request.getHeaders().add(COOKIE, cookie);
            }
        }
    }
    // execute the request
    ClientHttpResponse response = execution.execute(request, byteArray);
    // pull any cookies off and store them
    cookies = response.getHeaders().get(SET_COOKIE);
    if (cookies != null) {
        for (String cookie : cookies) {
            Log.d(getClass().getSimpleName(), ">>> response cookie = " + cookie);
        }
        StaticCacheHelper.storeObjectInCache(COOKIE_STORE, cookies);
    }
    Log.d(getClass().getSimpleName(), ">>> leaving intercept");
    return response;
}

}

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

Добавить перехватчик в шаблон запроса:

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClientHelper.createDefaultHttpClient(GET_SERVICE_URL)));
ClientHttpRequestInterceptor[] interceptors = {new MyClientHttpRequestInterceptor()};
restTemplate.setInterceptors(interceptors);

И вот, пожалуйста! Я проверил это, и это работает. Это должно сдерживать вас до Spring Android 1.0.0.RC1, когда мы можем использовать HttpContext напрямую с RestTemplate.

Надеется, что это помогает другим !!

...