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回调 供我们持久化

相关推荐
孤客网络科技工作室20 小时前
AJAX 全面教程:从基础到高级
android·ajax·okhttp
Liknana6 天前
OKHTTP断点续传
android·okhttp·面试
爱编程的鱼9 天前
web前后端交互方式有哪些?
前端·okhttp
鞠崽2333310 天前
【六袆 - WebSocket】WebSocket的认识;一次AJAX请求模型;一次长轮询请求模型;一次WebSocket请求模型;
websocket·ajax·okhttp
吃汉堡吃到饱12 天前
【Android】浅析OkHttp(1)
android·okhttp
wa的一声哭了16 天前
黑马JavaWeb-day03
数据结构·c++·人工智能·深度学习·算法·okhttp·eclipse
小R资源16 天前
Django CSRF Token缺失或不正确
okhttp·django·csrf
我就说好玩23 天前
ajax嵌套ajax实现不刷新表单并向指定页面二次提交数据
android·ajax·okhttp
Ther23323 天前
SpringBoot中OKHttp和压缩文件的使用
okhttp
ShyTan24 天前
Java工具类--OkHttp工具类
数据库·okhttp