关于java:写给android工程师的cookie分析

2次阅读

共计 5960 个字符,预计需要花费 15 分钟才能阅读完成。

前言

很快乐遇见你~

cookie 在 HTTP1.1 版本中被增加,目标是为了解决 HTTP 的 无状态个性,使 HTTP 变得“有状态”。

咱们在做 android 开发,很多时候并不能很好了解 cookie 的存在价值、优化。这其实失常。Http 中文翻译为超文本传输协定,是为 web 开发的传输协定。而 cookie 作为其中的一个性能,他的设计天然也是服务于 web。为了更好地了解 cookie 他的实质,须要站在 web 开发的角度来了解他,具体而言就是应用浏览器网页具体化客户端。

android 的倒退要比 web 晚的多。尽管都应用 cookie,但 cookie 绝对于 android 并没有跟 web 一样有那么多的益处。但 cookie 也并不是齐全没用,例如咱们都晓得 cookie 能够实现记住登录状态。

这篇的文章的目标在于帮忙 android 开发者了解 cookie 的实质是什么,这样咱们在应用 cookie 的时候会更加心里有底。

购物车的故事

咱们都晓得,HTTP 是一个无状态的协定。无状态体现在每一次的申请与响应都是独立的,他们之间不会相互记录、相互影响。

嗯?咱们在后端不是会保护购物车的状态,提供 api 给客户吗?是的,能够有,咱们后端齐全能够应用数据结构来存储购物车信息。但这不属于 HTTP 的领域了,单纯的 HTTP 是无奈实现这样的性能的,是吧?HTTP 的每一次申请都是独立、不相互影响的,他不会记得上一次拜访发送了什么返回了什么。

为了记住操作状态,http 设计者在申请头中附加一些信息,来辨认咱们的操作状态:

客户端每次操作购物车,服务端都会为其生成一个字段,并把这个字段发送给客户端。这个字段存储了客户端后面的操作状态,下次客户端申请服务端的时候只须要把这个字段附加在申请头中,服务端通过解析这个字段的内容,就晓得后面的申请都做了什么。

例如上图,客户端增加了一个橘子,服务端生成【orange=1】并施展给客户端存储。客户端下一次操作只须要把【orange=1】附加在申请头中,服务端就晓得客户曾经增加一个橘子在购物车了。

到这里曾经很显著了,这个字段就是cookie

cookie 由服务端生成,在客户端中存储;客户端每次申请附加上 cookie,服务端通过解析 cookie 的内容实现状态记录

整个 cookie 的机制中,客户端做的事件很简略:存储 cookie,附加 cookie。客户端不参加 cookie 的生成,也不参加 cookie 的批改。而服务端则负责cookie 的生成与解析,然而不负责存储。

cookie 的实现

cookie 在 HTTP 中通过两个头部字段来实现:cookie 和 set-cookie

set-cookie在响应报文中附带,告知客户端须要存储该 cookie。例如 set-cookie:orange=1cookie 是申请报文头部的一个字段,附带上服务端生成的 cookie,例如cookie:orange=1

sequenceDiagram
客户端 ->> 服务端: 增加一个橘子
服务端 -->> 客户端: set-cookie:orange=1
客户端 ->> 服务端: cookie:orange=1,增加两个香蕉
服务端 -->> 客户端: set-cookie:orange=1&bananer=2

在 cookie 机制中,客户端和服务端都必须各司其职。

  • 客户端,具体而言就是浏览器,他须要主动把一个网站的生成的 cookie 存储下来,下一次申请主动把 cookie 附加在申请头中。
  • 服务端,在收到 cookie 字段时,须要主动解析并响应对应的后果。如收到 orange= 1 必须主动解析进去增加了一个橘子,再次增加香蕉就是 orange=1&bananer=2。

同时,cookie 是能够领有多个的,也就是能够不止有一个 cookie,这样一个网站就能够应用 cookie 同时记录多个状态。我这里咱们应用 postman 看一下百度网站的 cookie:

能够看到百度返回了多达 6 个 set-cookie。此外,咱们能够通过浏览器来查看一个网站的 cookie:

能够看到百度这个网站累积应用多达 37 个 cookie。点击 cookie 就能够查看具体的内容了。

同时须要留神的是,cookie 个别不会明文在网络中传输,而是会进行加密。这在晚期没有 https 的状况下是十分重要的,否则网络中任何节点都有可能劫持到咱们的 cookie。下面再 postman 中百度网站的 Set-Cookie 字段就能够很显著看的进去是通过了加密。

上面咱们再通过两个例子来进一步了解 cookie。

主题偏好性能

有一些网站具备的一个性能是:不须要登录,然而却能够记住咱们的抉择的主题,例如暗色或亮色;下一次拜访还是咱们上次的抉择。咱们当然能够在后端为每一个 ip 和端口记录抉择后果,但越来越多的访问量占用的空间很大、查问的性能也收到了影响。这个时候就轮到 cookie 上场了,应用 cookie 即可更加轻量级地实现这个性能。他的性能模型如下图:

sequenceDiagram
客户端 ->> 服务端: 设置暗色主题
服务端 -->> 客户端: set-cookie:theme=dark
客户端 ->> 服务端: cookie:theme=dark
服务端 -->> 客户端: 返回暗色主题的网页

当咱们设置主题的时候,服务端会生成一个主题偏好的 cookie 交给咱们存储。下一次只须要把 cookie 附加在申请头中,服务端解析 cookie 中的内容,就能够返回对应主题的网页了。相比与在服务端应用数据结构来存储用户的信息,这种形式是不是更加轻量、更加简略?且无需登录注册既能够记住本人的主题偏好。

这种性能在 android 中仿佛不太实用,因为咱们的主题应用的是本地的配置,界面的设计内容也都是存储在本地,因此无需服务端来为咱们记住主题偏好问题。

但站在 web 的角度这个性能就十分实用了。网页的具体内容都是存储在服务端,而浏览器只负责渲染界面。这个时候须要 cookie 来通知服务端,上次我抉择了什么样的主题,这次你也给我返回一样主题的网页界面。

记住登录状态

嘿,登录注册,咱们 android 工程师,就很好了解了。毕竟第一次接触到 cookie 可能都是应用他来实现记住登录状态,笔者就是如此。当然,在商业我的项目中,因为 cookie 设计的不安全性,并不会拿来当记住登录状态的伎俩,这是后话了,属于登录注册更加具体的内容。先来看看 cookie 是如何实现记住登录状态的:

sequenceDiagram
客户端 ->> 服务端: user=admin&password=123123
服务端 -->> 客户端: set-cookie:sessionId=ABC123
客户端 ->> 服务端: cookie:sessionId=ABC123
服务端 -->> 客户端: 返回暗色主题的网页
  1. 当咱们拜访服务端进行登录之后,服务端会为咱们创立一个 session,会话。
  2. 服务端把 sessionId 放在 cookie 中返回给咱们。
  3. 服务端下一次申请服务器的时候把 cookie 附加在申请头中。
  4. 服务端通过解析其中的 sessionId,找到对应的 session,就晓得咱们曾经登录了且能够辨认咱们的身份,因为咱们的登录信息都记录在 session 中。

此外还有另外一种比拟不便但不太平安记住登录的形式:间接把用户名和明码通过某种加密伎俩加密后存储在 cookie 中,交由浏览器存储。这种非常简单粗犷,对于加密算法的要求比拟高。通常这种类型的 cookie 都有一个有效期,过期之后则必须从新登录。

在 web 的环境下,应用 cookie 来记住登录还是不太平安的,上面理解一下 cookie 具备哪些毛病。

cookie 的毛病

cookie 的毛病在于他的 主动 个性,无论是浏览器主动存储和附加 cookie,还是服务器自动识别 cookie 并间接响应,都是不平安的。举几个例子来了解一下。

如果一个银行网站,应用 cookie 来记录登录状态。当咱们拜访攻击者的网站是,网站的 js 脚本能够间接拜访该银行的转账接口。浏览器会主动帮咱们把银行网站的 cookie 附加上,而银行服务端解析到咱们的 cookie,判断在登录中,就间接把钱转账了。

又比方,本地 js 脚本拿到咱们存储的 cookie 之后,就能够代替咱们的身份去拜访各个网站。和下面一样,服务端只有看到 cookie,就判断是咱们自己在操作了。

此外,web bugs 这一类的问题也导致了咱们的隐衷泄露问题 – 简略来说就是把咱们的拜访网站信息存储在 cookie 中,而后申请收集信息的服务器,该服务器通过解析 cookie 就能够收集到咱们的浏览信息,能够给咱们精准投递广告。这种计划也常用语浏览器行为跟踪,在非法的范畴内有助于进步咱们的网络服务质量,但难免会有不法分子整一些不好的操作。

针对这些毛病,HTTP 也进行了一些优化。例如 httpOnly 头部,设置有这个头部的响应报文,会让本地 js 脚本无奈拿到 cookie,从而保障了平安。但这个个性须要浏览器进行配合,如果浏览器没有这个实现,仍旧是不平安的。

但这些毛病,在 android 这个环境中是人造平安的。因为咱们的 app 不会被植入脚本代码,也不存在被其余的程序拿到 cookie 的状况。

在 android 中应用

首先明确一点是,客户端对于 cookie 的操作是很无限的。咱们只须要负责两件事:对网站的 cookie 进行存储,在申请的时候附加上 cookie

这里须要留神的是不同网站的 cookie 是须要离开的,不要把逻辑写死每次都附加上所有的 cookie。但对于自家的 app 个别后端只有自家的服务器,那么这个也就相对来说无关紧要。

android 开发的网络框架个别用的都是 okHttp,上面剖析一下如何应用 okHttp 来进行 cookie 操作。okHttp 框架默认是不实现 cookie 存储的。如果须要操作 cookie,那么有两个办法:应用 okHttp 的 cookie 存储接口、应用拦截器

应用 cookie 存储接口

okHttp 预留了一个接口让咱们能够很不便地进行 cookie 操作。咱们能够通过调用 okHttpClient 的办法来实现,如下代码:

val okHttpBuilder = OkHttpClient.Builder()
okHttpBuilder.apply {
    cookieJar(object : CookieJar{override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {// 实现存储 cookie 的逻辑}
                override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {// 实现增加 cookie 的逻辑}
            })
}

CookieJar 是一个接口,这个接口有两个办法别离对应于存储和增加 cookie。在收到响应报文时,okHttp 会把响应头部的 cookie 取出来,并回调 saveFromResponse 办法;在发动一个申请的时候,会调用 loadForRequest 来返回一个 cookie 列表,用于增加到申请头部中。

因而咱们只须要在创立 OkHttpClient 的时候,设置好 cookie 的回调监听即可。

这里须要留神的是,不论是间接应用 OkHttp 还是 Retrofit,都尽量放弃 OkHttpClient 全局单例,这样配置的 cookie 逻辑才不会生效。Retrofit 可通过上面的办法来自定义 okHttpClient:

mRetrofit = Retrofit.Builder()
            .client(okHttpBuilder.build())
            .baseUrl(BASE_URL)
            .build()

应用拦截器

okHttp 在发送一个申请会经验一系列的拦截器。拦截器能够简略了解为,每一次申请收回去会通过咱们配置的拦截器,返回的时候也会通过咱们设置的拦挡。这样咱们就能够在申请时把申请拦挡下来增加 cookie 之后再发送进来;而后在响应的时候,把 cookie 取下来,再把响应报文返回。如下代码:

class CookieInterceptor() :Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val requestBuilder = chain.request().newBuilder()
        // 为申请增加 cookie
        addCookie(requestBuilder)
        // 执行申请
        var response = chain.proceed(requestBuilder.build())
        // 为响应存储 cookie
        storeCookie(response)
        return response
    }
}

代码中 request 就是咱们的申请,调用 proceed 办法之后就会发动申请并拿到 response,之后咱们把 resp 中的 cookie 取下来,再把 response 返回即可。addCookiestoreCookie 办法须要咱们本人去实现,具体怎么实现就比拟灵便了,Room、SharePreference 都是能够的。

而后通过配置 client 来增加拦截器:

val okHttpBuilder = OkHttpClient.Builder()
okHttpBuilder.apply {addInterceptor(CookieInterceptor()))
}

这里咱们为 OkHttpClient 增加了一个咱们自定义的拦截器了。

这种办法比第一种间接应用 cookieJar 要简单一点,但拦截器的能做的事件比拟多,更加灵便。拦截器能够批改一个 request 中的所有内容,例如我把 baidu.com 全副重定向到google.com,拦截器是能够做到的,然而 cookieJar 只专与 cookie 存储。

对于拦截器的方面就不开展了,感兴趣的读者能够深刻去理解一下。

最初

与 HTTP 相干的很多货色,在肯定水平上都是为 web 端设计。学习的时候感觉云里雾里,可能是关上的形式不对。理解一点前端的常识,从 web 的角度来了解,再使用到 android 开发中,会是一个更好的姿态。

cookie 的性能更多的还是须要和后端配合,cookie 自身只是服务端生成,客户端存储,主动附加与解析的一个字段。在此之上要建设什么性能,则由开发者而定了。

针对于前端而言,这些对于 cookie 的常识必定是不够的,但对于 android 工程师曾经差不多,惯例的业务开发也曾经熟能生巧了。

金三银四,最近大家也都在春招吧,那就预祝各位能够顺利上岸,拿到喜爱的大厂 offer。

文章如果有帮忙,还心愿能够点个赞激励一下作者~

全文到此,原创不易,感觉有帮忙能够点赞珍藏评论转发。
有任何想法欢送评论区交换斧正。

如需转载请评论区或私信告知。

另外欢迎光临笔者的集体博客:传送门

正文完
 0