【问题标题】:Spring Android: using RestTemplate with https and cookiesSpring Android:使用带有 https 和 cookie 的 RestTemplate
【发布时间】:2023-03-05 17:13:01
【问题描述】:

我需要在来自 android 本机应用程序的 https 连接上使用 cookie。 我正在使用 RestTemplate。

检查其他线程 (例如Setting Security cookie using RestTemplate) 我能够在 http 连接中处理 cookie:

restTemplate.setRequestFactory(new YourClientHttpRequestFactory());

在哪里YourClientHttpRequestFactory extends SimpleClientHttpRequestFactory

这在 http 上可以正常工作,但在 https 上不行。

另一方面,我能够解决 Android 信任 SSL 证书的 https 问题:

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

这里描述了 HttpUtils: http://www.makeurownrules.com/secure-rest-web-service-mobile-application-android.html

我的问题是我需要使用 ClientHttpRequestFactory 的单个实现。 所以我有3个选择:

1) 找到一种使用 SimpleClientHttpRequestFactory 处理 https 的方法

2) 找到一种使用 HttpComponentsClientHttpRequestFactory 处理 cookie 的方法

3) 使用另一种方法

【问题讨论】:

  • 感谢 HttpUtils 链接!迫切需要 SSL 和其他提示的解决方案没有帮助。
  • 第二个链接在 2021 年断开 :'(

标签: android spring resttemplate


【解决方案1】:

我遇到了同样的问题。这是我的解决方案:

首先,我以与您相同的方式处理 SSL(我使用了 Bob Lee 的方法)。

Cookie 是另一回事。我过去在没有 RestTemplate 的情况下处理 cookie 的方式(即直接使用 Apache 的 HttpClient 类)是将 HttpContext 的实例传递给 HttpClient 的 execute 方法。让我们退后一步……

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

当然,如果您愿意,您可以在发送请求之前将 cookie 添加到 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类只是一个自定义类,可以将数据存储在静态Map中:

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 Spring Android 中的 RestTemplate 不允许您将 HttpContext 添加到请求的执行中!这在 Spring Framework 3.1.0.RELEASE 中得到了修复,修复是 scheduled to be migrated into Spring Android 1.0.0.RC1

所以,当我们获得 Spring Android 1.0.0.RC1 时,我们应该能够添加上面示例中描述的上下文。在此之前,我们必须使用 ClientHttpRequestInterceptor 从请求/响应标头中添加/拉取 cookie。

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 一起使用。

希望这对其他人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-16
    • 2018-11-23
    • 2012-08-15
    • 1970-01-01
    • 2013-07-11
    • 2016-08-29
    • 2011-12-27
    相关资源
    最近更新 更多