四种会话追踪技术

本贴最后更新于 1296 天前,其中的信息可能已经事过境迁

四种会话追踪技术

  • Cookie
  • Session
  • URL 重写
  • 隐藏表单域

Session 和 Cookie

  • session 用来表示用户会话,session 对象在服务端维护,一般 tomcat 设定 session 生命周期为 30 分钟,超时将失效,也可以主动设置无效。
  • cookie 存放在客户端,可以分为内存 cookie 和磁盘 cookie。内存 cookie 在浏览器关闭后消失,磁盘 cookie 超时后消失。当浏览器发送请求时,将自动发送对应 cookie 信息,前提是请求 url 满足 cookie 路径。
  • 可以将 sessionId 存放在 cookie 中,也可以通过重写 url 将 sessionId 拼接到 url 上。因此可以查看浏览器 cookie 或地址栏 url 看到 sessionId。
  • 请求到服务端时,将根据请求中的 sessionId 查找 session,如果可以获取到则返回,否则返回 null 或者返回新构建的 session,老的 session 依旧存在,请参考 API。

URL 重写

  • 可以通过 url 参数的形式将信息发送至服务器。
  • 但是这种方式参数的大小受到浏览器限制,cookie 禁用时可以继续的工作,不存在持久性,一旦页面关闭则结束。
  • 这种方式通过明文将信息传输,并不安全,容易被劫持。

隐藏表单域

表单隐藏域 type=hidden 的作用:

HTML 中写表单的时候,写入这段代码
<input type="hidden" name="#" value="#">
意思是在这里增加一个隐藏域。对于用户来说,在页面上隐藏域是不可见的。

  • 隐藏域的作用是帮助表单收集和发送信息,便于后端处理数据。用户点击提交数据的时候,隐藏域的信息也被一起发送到了后端。
  • 后端接收前端传来的数据,需要确认前端的身份,是本公司的网页传来的数据,而不是其他黑客知道后端地址后传来的假数据。那么就加一个隐藏域,验证 value 里的值和数据库里 name 的值是不是对应的,类似于“天王盖地虎,宝塔镇河妖”,暗号对的上,才能证明是自己人。当然这些东西也能用 cookie 实现,但使用隐藏域比较简单,而且不会有浏览器不支持,用户禁用 cookie 的烦恼。
  • 有时候一个表单里有多个提交按钮,后端怎么知道用户是点击哪个按钮提交过来的呢?这时候我们只要加隐藏域,对每一个按钮起个名字(value 值),后端接收到数据后,检查 value 值,就能知道是哪个按钮提交的了。
  • 有时候一个网页中有多个 form,我们知道多个 form 是不能同时提交的,但有时这些 form 确实相互作用,我们就可以在 form 中添加隐藏域来使它们联系起来。
  • JavaScript 不支持全局变量,但有时我们必须用全局变量,我们就可以把值先存在隐藏域里,它的值就不会丢失了。
  • 还有个例子,比如按一个按钮弹出四个小窗口,当点击其中的一个小窗口时其他三个自动关闭.可是 IE 不支持小窗口相互调用,所以只有在父窗口写个隐藏域,当小窗口看到那个隐藏域的值是 close 时就自己关掉。

例题

1.

有关会话跟踪技术描述正确的是?

  • A Cookie 是 Web 服务器发送给客户端的一小段信息,客户端请求时,可以读取该信息发送到服务器端。
  • B 关闭浏览器意味着临时会话 ID 丢失,但所有与原会话关联的会话数据仍保留在服务器上,直至会话过期。
  • C 在禁用 Cookie 时可以使用 URL 重写技术跟踪会话。
  • D 表单隐藏域将字段添加到 HTML 表单并在客户端浏览器中显示。
答案

A, B, C

解析
  • A 参考本二级标题知识点
  • B session 存在服务器,但是 sessionid 存在客户端,作为客户端找到 session 的引用。
  • C 可以通过重写 url 将 sessionId 拼接到 url 上。
  • D 表单隐藏域用来收集或发送信息的不可见元素,对于网页的访问者用户来说,表单隐藏域是看不见的。当表单被提交时,表单隐藏域会将其中定义的名称和值发送到服务器上。

jsessionid

jsessionid 是 session 的标识。这就好比每个人都有身份证一样。 (jsessionid 只是 tomcat 中对 session id 的叫法,在其它容器里面,不一定就是叫 jsessionid 了)。

Q&A

  1. 是不是只要一打开一个页面就会产生一个 jsessionid?
  2. 在不关闭浏览器的情况下,什么时候 jsessionid 会改变?我登陆后,登陆然后退出,jsessionid 会有什么变化?
  3. session 和 jsessionid 有什么关系?

所谓 session 可以这样理解:当与服务端进行会话时,比如说登陆成功后,服务端会为用户开壁一块内存区间,用以存放用户这次会话的一些内容,比如说用户名之类的。那么就需要一个东西来标志这个内存区间是你的而不是别人的,这个东西就是 session id (jsessionid 只是 tomcat 中对 session id 的叫法,在其它容器里面,不一定就是叫 jsessionid 了)。而这个内存区间可以理解为 session。

然后,服务器会将这个 session id 发回给你的浏览器,放入你的浏览器的 cookies 中(这个 cookies 是内存 cookies,跟一般的不一样,它会随着浏览器的关闭而消失)。

之后,只有你浏览器没有关闭,你每向服务器发请求,服务器就会从你发送过来的 cookies 中拿出这个 session id,然后根据这个 session id 到相应的内存中取你之前存放的数据。

但是,如果你退出登陆了,服务器会清掉属于你的内存区域,所以你再登的话,会产生一个新的 session 了。

jsessionid 解释的解释如下:

这是一个保险措施 因为 Session 默认是需要 Cookie 支持的,但有些客户浏览器是关闭 Cookie 的。而 jsessionid 是存储在 Cookie 中的,如果禁用 Cookie 的话,也就是说服务器那边得不到 jsessionid,这样也就没法根据 jsessionid 获得对应的 session 了,获得不了 session 就得不到 session 中存储的数据了。

这个时候就需要在 URL 中指定服务器上的 session 标识,也就是类似于"jsessionid=5F4771183629C9834F8382E23BE13C4C"这种格式。
用一个方法(忘了方法的名字)处理 URL 串就可以得到这个东西,这个方法会判断你的浏览器是否开启了 Cookie,如果他认为应该加他就会加上去。

Q:是不是只要一打开一个页面就会产生一个 jsessionid?

A:显然不是的。session 是有一定作用域的,而且是有时间限制的。

Q:在不关闭浏览器的情况下,什么时候 jsessionid 会改变?我登陆后,登陆然后退出,jsessionid 会有什么变化?

A:jsessionid 是服务器那边生成的,因为 cookie 是服务器那边送到客户端的信息。不管能不能修改 jsessionid,都不应该修改,如果你修改了,这就失去了 jessionid 的自身意义了,你修改的话,你让服务器那边如何找到对应的 session?找不到的话,你存放在那个 session 中的数据不是取不到了吗?

登陆然后退出,我认为会重新生成一个 jsessionid。因为退出的话,application 作用域的数据都会丢失,更何况这个比它作用域还小的 session?既然 session 都消失了,这个 jsessionid 有什么用?

Q:session 和 jsessionid 有什么关系?

A:jsessionid 是 session 的标识。这就好比每个人都有身份证一样。

cookie 和 session 机制区别与联系

具体来说 cookie 机制采用的是在客户端保持状态的方案,而 session 机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以 session 机制可能需要借助于 cookie 机制来达到保存标识的目的,但实际上它还有其他选择。

cookie 机制

正统的 cookie 分发是通过扩展 HTTP 协议来实现的,服务器通过在 HTTP 的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的 cookie。然而纯粹的客户端脚本如 JavaScript 或者 VBScript 也可以生成 cookie。而 cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的 cookie,如果某个 cookie 所声明的作用范围大于等于将要请求的资源所在的位置,则把该 cookie 附在请求资源的 HTTP 请求头上发送给服务器。

cookie 的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成 cookie 的作用范围。若不设置过期时间,则表示这个 cookie 的生命期为浏览器会话期间,关闭浏览器窗口,cookie 就消失。这种生命期为浏览器会话期的 cookie 被称为会话 cookie。会话 cookie 一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把 cookie 保存到硬盘上,关闭后再次打开浏览器,这些 cookie 仍然有效直到超过设定的过期时间。存储在硬盘上的 cookie 可以在不同的浏览器进程间共享,比如两个 IE 窗口。而对于保存在内存里的 cookie,不同的浏览器有不同的处理方式。

session 机制

session 机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。

当程序需要为某个客户端的请求创建一个 session 时,服务器首先检查这个客户端的请求里是否已包含了一个 session 标识(称为 session id),如果已包含则说明以前已经为此客户端创建过 session,服务器就按照 session id 把这个 session 检索出来使用(检索不到,会新建一个),如果客户端请求不包含 session id,则为此客户端创建一个 session 并且生成一个与此 session 相关联的 session id,session id 的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id 将被在本次响应中返回给客户端保存。

保存这个 session id 的方式可以采用 cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个 cookie 的名字都是类似于 SEEESIONID。但 cookie 可以被人为的禁止,则必须有其他机制以便在 cookie 被禁止时仍然能够把 session id 传递回服务器。

经常被使用的一种技术叫做 URL 重写,就是把 session id 直接附加在 URL 路径的后面。还有一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把 session id 传递回服务器。

Cookie 和 Token

概述

HTTP 是一个“无状态”协议,这意味着 Web 应用程序服务器在响应客户端请求时不会将多个请求链接到任何一个客户端。然而,许多 Web 应用程序的安全和正常运行都取决于系统能够区分用户并识别用户及其权限。

这就需要一些机制来为一个 HTTP 请求提供状态。它们使站点能够在会话期间对各用户做出适当的响应,从而保持跟踪用户在应用程序中的活动(请求和响应)。

cookie 和 token

下面两图大致展示了基于 cookie 和基于 token 工作流程。

cookie 工作流程

token 工作流程

基于 cookie 的身份验证

cookie 是源自站点并由浏览器存储在客户计算机上的简单文件。它们通常包含一个名称和一个值,用于将客户端标识为对站点具有特定许可权的特定用户。

cookie 与源域相连接的方式可以确保仅源域能够访问其中存储的信息。第三方服务器既不能读取也不能更改用户计算机上该域的 cookie 内容。

网景公司的前雇员于 1993 年发明了 cookie。

基于 cookie 的验证是有状态的,就是说验证或者会话信息必须同时在客户端和服务端保存。这个信息服务端一般在数据库中记录,而前端会保存在 cookie 中。

验证的一般流程如下:

  1. 用户输入登陆凭据;
  2. 服务器验证凭据是否正确,并创建会话,然后把会话数据存储在数据库中;
  3. 具有会话 id 的 cookie 被放置在用户浏览器中;
  4. 在后续请求中,服务器会根据数据库验证会话 id,如果验证通过,则继续处理;
  5. 一旦用户登出,服务端和客户端同时销毁该会话。

基于 token 的身份验证

随着单页面应用程序的流行,以及 Web API 和物联网的兴起,基于 token 的身份机制越来越被大家广泛采用。

当讨论基于 token 的身份验证时,一般都是说的 JSON Web Tokens(JWT)。虽然有着很多不同的方式实现 token,但是 JWT 已经成为了事实上的标准,所以后面会将 JWT 和 token 混用。

基于 token 的验证是无状态的。服务器不记录哪些用户已登陆或者已经发布了哪些 JWT。对服务器的每个请求都需要带上验证请求的 token。该标记既可以加在 header 中,可以在 POST 请求的主体中发送,也可以作为查询参数发送。

工作流程如下:

  1. 用户输入登陆凭据;
  2. 服务器验证凭据是否正确,然后返回一个经过签名的 token;
  3. 客户端负责存储 token,可以存在 local storage,或者 cookie 中;
  4. 对服务器的请求带上这个 token;
  5. 服务器对 JWT 进行解码,如果 token 有效,则处理该请求;
  6. 一旦用户登出,客户端销毁 token。

token 相对 cookie 的优势

无状态

基于 token 的验证是无状态的,这也许是它相对 cookie 来说最大的优点。后端服务不需要记录 token。每个令牌都是独立的,包括检查其有效性所需的所有数据,并通过声明传达用户信息。

服务器唯一的工作就是在成功的登陆请求上签署 token,并验证传入的 token 是否有效。

防跨站请求伪造(CSRF)

举个 CSRF 攻击的例子,在网页中有这样的一个链接
![](http://bank.com?withdraw=1000&to=tom),假设你已经通过银行的验证并且 cookie 中存在验证信息,同时银行网站没有 CSRF 保护。一旦用户点了这个图片,就很有可能从银行向 tom 这个人转 1000 块钱。

但是如果银行网站使用了 token 作为验证手段,攻击者将无法通过上面的链接转走你的钱。(因为攻击者无法获取正确的 token)

多站点使用

cookie 绑定到单个域。foo.com 域产生的 cookie 无法被 bar.com 域读取。使用 token 就没有这样的问题。这对于需要向多个服务获取授权的单页面应用程序尤其有用。

使用 token,使得用从 myapp.com 获取的授权向 myservice1.com 和 myservice2.com 获取服务成为可能。

支持移动平台

好的 API 可以同时支持浏览器,iOS 和 Android 等移动平台。然而,在移动平台上,cookie 是不被支持的。

性能

一次网络往返时间(通过数据库查询 session 信息)总比做一次 HMACSHA256 计算的 Token 验证和解析要费时得多。

JWT

JWT 是 JSON Web Token 的缩写。它定义了一种紧凑且独立的方式,用于将各方之间的信息安全地传输为 JSON 对象。这是一个开放的标准,见 RFC 7519

基于 JWT 的信息可以通过数字签名进行校验。校验的方法即可以使用消息摘要(HMAC),或者非对称加密(RSA)。

JWT 具有两个特点:

  1. 紧凑。由于其较小的尺寸,JWT 可以通过 URL,POST 参数或者 HTTP 头发送。较小的尺寸会带来传输速度的优势;
  2. 自包含:token 中包含了用户的所有必须信息,避免了多次查询数据库的需要。

应用场景

以下是 JWT 有用的一些场景

  1. 验证:这是 JWT 最常用的场景。一旦用户登陆成功,每个后续的请求将包括 JWT,服务器在对 JWT 进行验证后,允许用户访问服务和资源。单点登陆是一个广泛使用 JWT 的场景,因为它的开销相对较小,并且能够在不同的域中轻松使用。
  2. 信息交换:JWT 是在可以安全地传输信息。因为 JWT 可以被签名,收信人可以确认发信人的身份,同时也能够验证内容是否被篡改。

格式

JWT 包括三个部分:头部、载荷和签名,这三个部分通过 . 连接起来。

因此,一个典型的 JWT 长这样 xxxxx.yyyyy.zzzzz

头部

头部通常包括两部分:token 类型(JWT),和使用到的算法,如 HMAC、SHA256 或 RSA,下面是一个例子,说明这是一个 JWT,使用的签名算法是 HS256。

{
  "alg": "HS256",
  "typ": "JWT"
}

头部会通过 Base64Url 编码形成 JWT 的第一部分。

载荷

第二部分是载荷,要传递出去的声明,其中包含了实体(通常是用户)和附加元数据。有三种类型的声明:

  • 保留声明:这是一组预定义的声明,非强制性,用来帮助接收方(服务器)更好地理解这个 JWT。其中包括:iss(issuer,该 JWT 的签发者),exp(expiration time,过期时间),sub(subject,该 JWT 所面向的用户),aud(audience,JWT 的接收者),和另外一些声明
  • 公共声明:这些可以用使用 JWT 的人随意定义。但是为了避免冲突,应在在 IANA JSON WEB 令牌注册表中定义它们,或者将其定义为包含防冲突命名空间的 URI。
  • 私有声明:这些是为了在同意使用它们的各方之间共享信息而创建的自定义声明。

下面是一个例子

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

载荷会通过 Base64Url 编码形成 JWT 的第二部分

签名

将上面两部分编码后,使用 . 连接在一起,形成了 xxxxx.yyyyyy。

最后,采用头部指定的算法,和私钥对上面的字符串进行签名。

加入采用的是 HMAC SHA256 算法,签名将通过下面的方式生成

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

该签名用户验证 JWT 发送者的身份,并确保该消息没有被篡改。

JWT 工作流程

在身份验证过程中,一旦用户使用其凭据成功登陆,服务器将返回 JWT,该 JWT 必须在客户端本地保存。这和服务器创建会话并返回 cookie 的传统方法不同。

每次用户要请求受保护的资源时,必须在请求中带上 JWT。通常在 Authorization 头 Bearer 中,如下:

Authorization: Bearer <token>

这是一种无状态的认证机制,因为用户状态永远不会保存在服务器内存中。服务器的受保护路由将在授权头中检查有效的 JWT,如果存在,则允许用户访问受保护的资源。由于 JWT 是自说明的,包含了所有必要的信息,这就减少了多次查询数据库的需要。

这样可以完全依赖无状态的数据 API,甚至可以向下游服务发出请求。API 的作用域并不重要,因此跨源资源共享(CORS)不会是一个问题,因为它不使用 Cookie。

整个流程如下图:

img

使用 JWT 的理由

现在来谈谈 JWT 与简单网页令牌(SWT)和安全断言标记语言令牌(SAML)相比的优势。

由于 JSON 比 XML 更短小,编码时其大小也较小,使得 JWT 比 SAML 更紧凑。这使得 JWT 成为在 HTML 和 HTTP 环境中能更快地传递。

从安全角度来说,SWT 只能通过使用 HMAC 算法的共享密钥进行对称签名。但是,JWT 和 SAML 令牌可以以 X.509 证书的形式使用公钥/私钥对进行签名。与简单的 JSON 签名相比,使用 XML 数字签名签名 XML 而不引入模糊的安全漏洞是非常困难的。

JSON 解析器在大多数编程语言中很常见,因为它们直接映射到对象。相反,XML 没有自然的文档对对象映射。这使得使用 JWT 比 SAML 断言更容易。

从使用平台来说,JWT 在 Internet 规模上使用。这突出了客户端处理多个平台上特别是移动平台上的 JSON Web 令牌的便利性。

引用/参考

"牛客 294719 号"的回答 - 牛客

表单隐藏域 type=hidden 的作用 - 辉夜乀 - 简书

"半纸流年-轻描了谁的夏天"的回答 - 牛客

为什么会有 jsessionid,这个东东有什么用呢? - masanpaossa - OSCHINA

Cookie 和 Token - 大蟒传奇 - 简书

2 操作
JellyfishMIX 在 2020-09-30 23:41:47 更新了该帖
JellyfishMIX 在 2020-09-30 23:40:49 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...