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

相关推荐
Jeled18 小时前
Android 网络层最佳实践:Retrofit + OkHttp 封装与实战
android·okhttp·kotlin·android studio·retrofit
allk554 天前
OkHttp源码解析(一)
android·okhttp
allk554 天前
OkHttp源码解析(二)
android·okhttp
aFakeProgramer4 天前
拆分PDF.html 办公小工具
okhttp
一壶浊酒..5 天前
ajax局部更新
前端·ajax·okhttp
洛克大航海8 天前
Ajax基本使用
java·javascript·ajax·okhttp
whltaoin14 天前
Java 网络请求 Jar 包选型指南:从基础到实战
java·http·okhttp·网络请求·retrofit
华农第一蒟蒻15 天前
谈谈跨域问题
java·后端·nginx·安全·okhttp·c5全栈
一直向钱17 天前
android 基于okhttp的socket封装
android·okhttp
linuxxx11017 天前
ajax回调钩子的使用简介
okhttp