• OKHTTP:Cookie在okhttp中的原理


    Retrofit和Okhttp添加cookie - 掘金

    使用CookieJar把cookie添加到请求中

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

    1. class AnalysisCookie : CookieJar {
    2. override fun saveFromResponse(url: HttpUrl, cookies: MutableList) {
    3. }
    4. override fun loadForRequest(url: HttpUrl): MutableList {
    5. val cookies = ArrayList()
    6. val token = SharedPreferenceUtil.getStringSPAttrs(ContextUtil.getAppContext(), SharedPreferenceUtil.AttrInfo.USER_TOKEN, "")
    7. token.doThis {
    8. cookies.add(Cookie.Builder()
    9. .domain("your domain")
    10. .path("/")
    11. .name("your cookieKey")
    12. .value("your cookieValue")
    13. .httpOnly()
    14. .secure()
    15. .build())
    16. }
    17. return cookies
    18. }
    19. }

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

    1. val okHttpBuilder = OkHttpClient.Builder()
    2. .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
    3. .readTimeout(TIME_READ, TimeUnit.SECONDS)
    4. .writeTimeout(TIME_WRITE, TimeUnit.SECONDS)
    5. .cookieJar(AnalysisCookie())

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

    什么是CookieJar

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

    1. OkHttpClient(Builder builder) {
    2. ......
    3. this.cookieJar = builder.cookieJar;
    4. this.cache = builder.cache;
    5. this.internalCache = builder.internalCache;
    6. this.socketFactory = builder.socketFactory;
    7. ......
    8. }

    CookieJar是如何生效的

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

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

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

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

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

    1. @Override public Response execute() throws IOException {
    2. synchronized (this) {
    3. if (executed) throw new IllegalStateException("Already Executed");
    4. executed = true;
    5. }
    6. captureCallStackTrace();
    7. timeout.enter();
    8. eventListener.callStart(this);
    9. try {
    10. client.dispatcher().executed(this);
    11. Response result = getResponseWithInterceptorChain();
    12. if (result == null) throw new IOException("Canceled");
    13. return result;
    14. } catch (IOException e) {
    15. e = timeoutExit(e);
    16. eventListener.callFailed(this, e);
    17. throw e;
    18. } finally {
    19. client.dispatcher().finished(this);
    20. }
    21. }

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

      Response result = getResponseWithInterceptorChain();

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

    1. Response getResponseWithInterceptorChain() throws IOException {
    2. // Build a full stack of interceptors.
    3. List interceptors = new ArrayList<>();
    4. interceptors.addAll(client.interceptors());
    5. interceptors.add(retryAndFollowUpInterceptor);
    6. interceptors.add(new BridgeInterceptor(client.cookieJar()));
    7. interceptors.add(new CacheInterceptor(client.internalCache()));
    8. interceptors.add(new ConnectInterceptor(client));
    9. if (!forWebSocket) {
    10. interceptors.addAll(client.networkInterceptors());
    11. }
    12. interceptors.add(new CallServerInterceptor(forWebSocket));
    13. Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
    14. originalRequest, this, eventListener, client.connectTimeoutMillis(),
    15. client.readTimeoutMillis(), client.writeTimeoutMillis());
    16. return chain.proceed(originalRequest);
    17. }

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

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

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

    1. @Override public Response intercept(Chain chain) throws IOException {
    2. ......
    3. List cookies = cookieJar.loadForRequest(userRequest.url());
    4. if (!cookies.isEmpty()) {
    5. requestBuilder.header("Cookie", cookieHeader(cookies));
    6. }
    7. ......
    8. return responseBuilder.build();
    9. }

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

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

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

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

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

    1. class CookieInterceptor : Interceptor {
    2. override fun intercept(chain: Interceptor.Chain): Response {
    3. val builder = chain.request().newBuilder()
    4. val cookie = "cookieName=cookieValue"
    5. builder.addHeader("Cookie", cookie)
    6. return chain.proceed(builder.build())
    7. }
    8. }

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

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

    Cookie持久化:

    https://www.jianshu.com/p/f1df12ceaedc


    可以使用工具类:在zhy封装的okhttpUtils项目里的类

    https://github.com/hongyangAndroid/okhttputils/blob/master/okhttputils/src/main/java/com/zhy/http/okhttp/cookie/store/MemoryCookieStore.java

    1、SerializableHttpCookie(持久化到本地sp文件中)

    2、MemoryCookieStore(持久化在内存中)

  • 相关阅读:
    人工智能基础:机器学习常见的算法介绍
    k8s之service
    视频编辑软件Premiere Pro 2022 mac(pr2023)v22.6.2中文功能
    VMwareCentOS7Ping 指令报错:Nameorservicenotknown
    【Vue 开发实战】基础篇 # 14:template和JSX的对比以及它们的本质
    XML简单介绍
    GCC 编译优化等级
    BRLWON Cobra
    java8 (jdk 1.8) 新特性——Stream ApI
    基于SSM+SpringBoot+VUE前后端分离的停车场管理系统
  • 原文地址:https://blog.csdn.net/cpcpcp123/article/details/126124460