okhttp cookiejar 原理浅析

HTTP协议本身是无状态的。什么是无状态呢,即服务器无法判断用户身份。

cookie就像一个身份证一样,是服务器颁发给客户端的,后续客户端就可以拿着这个绿卡通行,在请求的时候就可以获取到服务器的数据。

  1. 客户端发送登录请求

  2. 服务器响应:在响应报文之中通过报文首部的 Set-Cookie首部字段颁发 cookie字段给客户端

  3. 客户端获取到set-cookie字段(代表登陆成功)

    客户端解析set-cookie字段

  4. 客户端在下一次请求时候在请求报文之中通过 cookie 首部 设置cookie来获得服务端的数据

某api接口登录时返回的set-cookie 字段

ini 复制代码
"MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/eapi/feedback; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/neapi/feedback; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/neapi/clientlog; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/weapi/clientlog; HTTPOnly;
​
MUSIC_SNS=; Max-Age=0; Expires=Fri, 10 Mar 2023 08:13:16 GMT; Path=/;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/eapi/clientlog; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/neapi/feedback; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/wapi/clientlog; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/api/clientlog; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/eapi/feedback; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/weapi/clientlog; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/api/feedback; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/wapi/clientlog; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/wapi/feedback; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/openapi/clientlog; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/openapi/clientlog; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/wapi/feedback; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/weapi/feedback; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/api/feedback; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/weapi/feedback; HTTPOnly;
​
MUSIC_A_T=1678353370003; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/eapi/clientlog; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/neapi/clientlog; HTTPOnly;
​
__csrf=80afb31b44803b11a3a1b6dc353a0a98; Max-Age=1296010; Expires=Sat, 25 Mar 2023 08:13:26 GMT; Path=/;;
​
MUSIC_U=49e11c32722be03b70e7c306f8729b04ca985a91a360e98db8a41af6e30166d5993166e004087dd3a0cad15c758e52c84916e0979136541ccaa6936aa865f618ddb65572a1118868d4dbf082a8813684; Max-Age=15552000; Expires=Wed, 06 Sep 2023 08:13:16 GMT; Path=/; HTTPOnly;
​
MUSIC_R_T=1678353370019; Max-Age=2147483647; Expires=Wed, 28 Mar 2091 11:27:23 GMT; Path=/api/clientlog; HTTPOnly"

各个字段的属性

max-age 与Expires 字段同时存在时 ,max-age字段有效

Path=/api/clientlog 路径:如果api访问到了这个路径 就把这个路径所需要的cookie 带上,其他的cookie则不用带上

如何解析? okhttp 的cookie类已经做了

如何判断cookie是否过期

  1. MUSIC_U 的过期时间是:Expires=Wed, 06 Sep 2023 08:13:16 GMT
  2. __csrf 的过期时间是:Expires=Sat, 25 Mar 2023 08:13:26 GMT

要判断登录是否过期,您需要将这两个过期时间与当前时间进行比较。如果当前时间晚于这两个过期时间中的任何一个,那么登录就被认为是过期的。

cookiejar

cookiejar是okhttp的一个接口,并且没有默认实现

  • saveFromResponse 方法会解析setCookie字段,并做回调

    • 在此方法中应该对回调的cookie实现保存和持久化,用于下一次的请求(整个Cookie 对象序列化成一个字符串)
  • loadForRequest方法返回一个List 并会解析到下一次请求报文的cookie首部

    • 在此方法中应该读取持久化过的cookie(将给定的经过序列化的字符串还原成一个 OkHttp3 Cookie 对象),并对cookie做特殊的过滤处理

      • 过期:登录过期,考虑清除持久化过的cookie并重新登录

      • 匹配path:若cookie与当前请求的路径相同,才在本次请求时带上,否则不带

第三方持久化与过滤方案

scss 复制代码
PersistentCookieJar cookieJar =
        new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context));
return new OkHttpClient.Builder()
        .cookieJar(cookieJar)
        .build();

依赖

arduino 复制代码
//cookie管理
implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'

框架做了cookie的持久化管理,与path不匹配的过滤,以及cookie过期的处理,但是并不能判断是否需要重新登陆

解决方案:每次进入app之后进行一次个人账户的请求,若返回的id不为空,则代表当前cookie并未过期

若为空,则重新登陆

原码解析

  1. getResponseWithInterceptorChain方法
scss 复制代码
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor

2.在OkhttpClient类中

ini 复制代码
 val cookieJar: CookieJar = builder.cookieJar

3.在Builder类中

csharp 复制代码
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES

4.具体NO_COOKIES 的实现

kotlin 复制代码
private class NoCookies : CookieJar {
  override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
  }
​
  override fun loadForRequest(url: HttpUrl): List<Cookie> {
    return emptyList()
  }
}

可以看到nocookies的实现是空实现,也就是没有具体作用的,这也是我们为什么要实现cookiejar的原因

5.具体到BridgeInterceptor(client.cookieJar)中

BridgeInterceptor 的作用

在请求之前:对请求报文首部字段的添加

请求之后:对获得的响应报文首部的去除,以及根据特殊特殊首部做处理,eg :Content-Encoding为gzip就需要对响应的body做解压缩处理

为什么我们要找到这个类:因为cookie也是首部字段

6.原码使用流程

scss 复制代码
//1.请求之前
val cookies = cookieJar.loadForRequest(userRequest.url)//拿到我们自定义持久化并已经过滤之后的cookie
if (cookies.isNotEmpty()) {//表明是我们自定义的cookiejar
  requestBuilder.header("Cookie", cookieHeader(cookies))//添加我们解持久化,并过滤的cookie为首部
}
......省略部分代码
//2.进行实际的网络请求
val networkResponse = chain.proceed(requestBuilder.build())
​
​
//3.请求之后
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)//从响应报文的set-cookie字段解析成cookie实例对象

//1

//3

解析后将url对象与cookie回调 供我们持久化

相关推荐
凌冰_2 天前
Vue 使用Ajax异步或同步
前端·ajax·okhttp
nee~2 天前
Charles抓包
okhttp
赵得C14 天前
AJAX拦截器失效排查指南:当你的beforeSend有效但error/complete沉默时
前端·ajax·okhttp
粤M温同学18 天前
Android OkHttp 框架超时设置详解
android·okhttp
粤M温同学18 天前
Android 使用OkHttp 下载文件失败问题定位和修复
okhttp
CUIYD_198918 天前
Ajax 核心知识点全面总结
前端·ajax·okhttp
weixin_4383354018 天前
Spring RestTemplate + MultiValueMap vs OkHttp 多值参数的处理
java·spring·okhttp
网络点点滴21 天前
上传一个菜谱-最后部分(项目完结)
android·okhttp
农业工作者21 天前
Android:使用OkHttp
android·okhttp
androidwork21 天前
Android 中 OkHttp 的自定义 Interceptor 实现统一请求头添加
android·java·okhttp·kotlin