Retrofit和Okhttp添加cookie

3,086 阅读4分钟

最近项目中需要在进行网络请求的时候添加cookie,项目中网络框架使用的是Retrofit+Okhttp,于是就研究了下,希望看到此文章的同学都有收获。

使用CookieJar把cookie添加到请求中

之前看过OkHttp的源码知道CookieJar,所以就使用它来添加请求中的cookie,首先定义一个CookieJar,代码如下:

class AnalysisCookie : CookieJar {

    override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
    }

    override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {
        val cookies = ArrayList<Cookie>()
        val token = SharedPreferenceUtil.getStringSPAttrs(ContextUtil.getAppContext(), SharedPreferenceUtil.AttrInfo.USER_TOKEN, "")
        token.doThis {
            cookies.add(Cookie.Builder()
                    .domain("your domain")
                    .path("/")
                    .name("your cookieKey")
                    .value("your cookieValue")
                    .httpOnly()
                    .secure()
                    .build())
        }
        return cookies
    }
}

然后在OkHttpClient生成的过程中把我们的CookieJar添加进去

  val okHttpBuilder = OkHttpClient.Builder()
                    .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
                    .readTimeout(TIME_READ, TimeUnit.SECONDS)
                    .writeTimeout(TIME_WRITE, TimeUnit.SECONDS)
                    .cookieJar(AnalysisCookie())

简单两步就可以把cookie添加到请求中了。

什么是CookieJar

什么是CookieJar呢,单从名字就可以看出CookieJar是跟cookie相关的,那么CookieJar是在哪里定义的呢?在OkHttp的源码中会找到它的蛛丝马迹

 OkHttpClient(Builder builder) {
    ......
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;
    ......
    }

CookieJar是如何生效的

那么CookieJar是怎么生效的呢?先看下OkHttp的简单使用

OkHttpClient okHttpBuilder = OkHttpClient.Builder();
final Request request = new Request.Builder().url("https://github.com/").build();
Response response = client.newCall(request).execute();

上面只是粗略的写法,仅供大家查看,在实际应用过程中还会添加很多的配置。从上面代码中发现,最终会执行OkHttpClient的newCall(request).execute()方法,本文不详细分析OkHttp的详细源码就不管execute()里面的逻辑了,让我们看一下newCall里面做了什么。

  /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

从上面会发现最终返回一个RealCall,那么RealCall又是什么呢?写到这里发现源码还是很多(手动狗头),说好不execute的最后发现还是避不开😭。让我们看看,下面是RealCall的execute()方法源码

 @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    timeout.enter();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      e = timeoutExit(e);
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

从上面会发现最终请求的Response是从这段代码中返回的,

  Response result = getResponseWithInterceptorChain();

细心的同学可能都发现了,到目前为止跟我们配置的CookieJar根本没关系啊,甚至OkHttpClient里面配置的东西也都没见到啊,别着急马上就是见证奇迹的时刻了,让我们看一下getResponseWithInterceptorChain()究竟做了什么。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

看到这里相信大家都有了眉目,别的不多说主要看这句

interceptors.add(new BridgeInterceptor(client.cookieJar()));

最终CookieJar会被加入到定义的BridgeInterceptor中,而BridgeInterceptor跟平时使用时添加的什么LoggerInterceptor之类的拦截器是一种东西,都是在请求进行之前添加自己的操作,让我们快到斩乱麻,看一下BridgeInterceptor究竟对CookieJar做了什么

 @Override public Response intercept(Chain chain) throws IOException {
    ......

    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }
    ......
    return responseBuilder.build();
  }

最终CookieJar会被添加到请求的Header中,

 /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
  private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
      if (i > 0) {
        cookieHeader.append("; ");
      }
      Cookie cookie = cookies.get(i);
      cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
  }

至此,cookie已经被添加到请求中了。

从CookieJar的使用思路中得到的另外一种添加cookie的方法

从上面代码中发现,cookie最终是以 builder.addHeader("Cookie", cookie)的方式添加到请求中的,那么我们可不可以不使用CookieJar,自己实现一个Interceptor直接添加呢?话不多说,直接撸码

 class CookieInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val builder = chain.request().newBuilder()
        val cookie = "cookieName=cookieValue"
        builder.addHeader("Cookie", cookie)
        return chain.proceed(builder.build())
    }
}

经过测试发现这种方式也是可行的,其实CookieJar只是对上面这种方式进行了封装,在具体使用时大家可以根据喜好才选择自己喜欢的方式。

到此本文也就写完了,看源码的过程总是比较累的,但是好处也是巨大的。知其然知其所以然,看的过程中也能学到很多东西,扩大我们的眼界。

PS:喜欢的同学可以扫码关注我的公众号呦