热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

什么是Cookie、Session、Token?

原文:https:mp.weixin.qq.comspWXhI_ppKhtOP-Xf_SpuDA来源:后厂村码农在了解这三个概念之前我们先要了解HTTP是无状态的Web服务器,什么

原文:https://mp.weixin.qq.com/s/pWXhI_ppKhtOP-Xf_SpuDA

来源:后厂村码农

  在了解这三个概念之前我们先要了解 HTTP 是无状态的Web服务器,什么是无状态呢?就是一次对话完成后下一次对话完全不知道上一次对话发生了什么。

  如果在 Web 服务器中只是用来管理静态文件还好说,对方是谁并不重要,把文件从磁盘中读取出来发出去即可。但是随着网络的不断发展,比如电商中的购物车只有记住了用户的身份才能够执行接下来的一系列动作。所以此时就需要我们无状态的服务器记住一些事情。

  那么 Web 服务器是如何记住一些事情呢?既然 Web 服务器记不住东西,那么我们就在外部想办法记住,相当于服务器给每个客户端都贴上了一个小纸条。上面记录了服务器给我们返回的一些信息。然后服务器看到这张小纸条就知道我们是谁了。那么 COOKIE 是谁产生的呢?COOKIEs 是由服务器产生的。

1. COOKIE

COOKIE 产生的过程

(1)浏览器第一次访问服务端时,服务器此时肯定不知道他的身份,所以创建一个独特的身份标识数据,格式为 key=value,放入到 Set-COOKIE 字段里,随着响应报文发给浏览器。

(2)浏览器看到有 Set-COOKIE 字段以后就知道这是服务器给的身份标识,于是就保存起来,下次请求时会自动将此 key=value 值放入到 COOKIE 字段中发给服务端。

(3)服务端收到请求报文后,发现 COOKIE 字段中有值,就能根据此值识别用户的身份然后提供个性化的服务。

技术分享图片

 

 

   接下来我们用代码演示一下服务器是如何生成,我们自己搭建一个后台服务器,这里用的是 Spring Boot 搭建的,并且写入 SpringMVC 的代码如下:


@RequestMapping("/testCOOKIEs")
public String COOKIEs(HttpServletResponse response){
response.addCOOKIE(
new COOKIE("testUser","xxxx"));
return "COOKIEs";
}

  项目启动以后我们输入路径 http://localhost:8005/testCOOKIEs,然后查看发的请求。可以看到下面那张图是我们首次访问服务器时发送的请求,可以看到服务器返回的响应中有 Set-COOKIE 字段。

  而里面的 key=value 值正是我们服务器中设置的值:

技术分享图片

 

 

  接下来我们再次刷新这个页面可以看到在请求体中已经设置了 COOKIE 字段,并且将我们的值也带过去了。这样服务器就能够根据 COOKIE 中的值记住我们的信息了:

技术分享图片

 

 

  接下来我们换一个请求呢?是不是 COOKIE 也会带过去呢?接下来我们输入路径 http://localhost:8005 请求。我们可以看到 COOKIE 字段还是被带过去了:

技术分享图片

 

 

  那么浏览器的 COOKIE 是存放在哪呢?如果使用的是 Chrome 浏览器的话,那么可以按照下面步骤:



  • 在计算机打开 Chrome



  • 在右上角,一次点击更多图标→设置



  • 在底部,点击高级



  • 在隐私设置和安全性下方,点击网站设置



  • 依次点击 COOKIE→查看所有 COOKIE 和网站数据



  然后可以根据域名进行搜索所管理的 COOKIE 数据。所以是浏览器替你管理了 COOKIE 的数据。如果此时你换成了 Firefox 等其他的浏览器,因为 COOKIE 刚才是存储在 Chrome 里面的,所以服务器又蒙圈了,不知道你是谁,就会给 Firefox 再次贴上小纸条。

技术分享图片

 

 

 

2.1 COOKIE 中的参数设置

  说到这里,应该知道了 COOKIE 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。所以 COOKIE 需要用一些其他的手段用来保护,防止外泄或者窃取,这些手段就是 COOKIE 的属性。

技术分享图片

 

 

 下面我就简单演示一下这几个参数的用法及现象:

2.1.1 Path

  设置为 COOKIE.setPath("/testCOOKIEs"),接下来我们访问 http://localhost:8005/testCOOKIEs,可以看到在左边和我们指定的路径是一样的。所以 COOKIE 才在请求头中出现,接下来我们访问 http://localhost:8005,我们发现没有 COOKIE 字段了,这就是 Path 控制的路径。

技术分享图片

 

 

 

2.1.2 Domain

  设置为 COOKIE.setDomain("localhost"),接下来我们访问 http://localhost:8005/testCOOKIEs。我们发现下图中左边的是有 COOKIE 的字段的,但是我们访问 http://172.16.42.81:8005/testCOOKIEs,看下图的右边可以看到没有 COOKIE 的字段了。这就是 Domain 控制的域名发送 COOKIE。

技术分享图片

 

 

   接下来的几个参数就不一一演示了,相信到这里大家应该对 COOKIE 有一些了解了。

 

2. Session

  COOKIE 是存储在客户端方,Session 是存储在服务端方,客户端只存储 SessionId。在上面我们了解了什么是 COOKIE,既然浏览器已经通过 COOKIE 实现了有状态这一需求,那么为什么又来了一个 Session 呢?这里我们想象一下,如果将账户的一些信息都存入 COOKIE 中的话,一旦信息被拦截,那么我们所有的账户信息都会丢失掉。 

  所以就出现了 Session,在一次会话中将重要信息保存在 Session 中,浏览器只记录 SessionId,一个 SessionId 对应一次会话请求。

技术分享图片

 

 

 


@RequestMapping("/testSession")
@ResponseBody
public String testSession(HttpSession session){
session.setAttribute(
"testSession","this is my session");
return "testSession";
}
@RequestMapping(
"/testGetSession")
@ResponseBody
public String testGetSession(HttpSession session){
Object testSession
= session.getAttribute("testSession");
return String.valueOf(testSession);
}

  这里我们写一个新的方法来测试 Session 是如何产生的,我们在请求参数中加上 HttpSession session。然后在浏览器中输入 http://localhost:8005/testSession 进行访问可以看到在服务器的返回头中在 COOKIE 中生成了一个 SessionId。然后浏览器记住此 SessionId 下次访问时可以带着此 Id,然后就能根据此 Id 找到存储在服务端的信息了。

技术分享图片

 

 

  此时我们访问路径 http://localhost:8005/testGetSession,发现得到了我们上面存储在 Session 中的信息。

那么 Session 什么时候过期呢?



  • 客户端:和 COOKIE 过期一致,如果没设置,默认是关了浏览器就没了,即再打开浏览器的时候初次请求头中是没有 SessionId 了。



  • 服务端:服务端的过期是真的过期,即服务器端的 Session 存储的数据结构多久不可用了,默认是 30 分钟。



  既然我们知道了 Session 是在服务端进行管理的,那么或许你们看到这有几个疑问,Session 是在哪创建的?Session 是存储在什么数据结构中?接下来带领大家一起看一下 Session 是如何被管理的。Session 的管理是在容器中被管理的,什么是容器呢?Tomcat、Jetty 等都是容器。

  接下来我们拿最常用的 Tomcat 为例来看下 Tomcat 是如何管理 Session 的。在 ManageBase 的 createSession 是用来创建 Session 的:


@Override
public Session createSession(String sessionId) {
//首先判断Session数量是不是到了最大值,最大Session数可以通过参数设置
if ((maxActiveSessions >= 0) &&
(getActiveSessions()
>= maxActiveSessions)) {
rejectedSessions
++;
throw new TooManyActiveSessionsException(sm.getString("managerBase.createSession.ise"), maxActiveSessions);
}
// 重用或者创建一个新的Session对象,请注意在Tomcat中就是StandardSession
// 它是HttpSession的具体实现类,而HttpSession是Servlet规范中定义的接口
Session session = createEmptySession();
// 初始化新Session的值
session.setNew(true);
session.setValid(
true);
session.setCreationTime(System.currentTimeMillis());
// 设置Session过期时间是30分钟
session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
String id
= sessionId;
if (id == null) {
id
= generateSessionId();
}
session.setId(id);
// 这里会将Session添加到ConcurrentHashMap中
sessionCounter++;
//将创建时间添加到LinkedList中,并且把最先添加的时间移除
//主要还是方便清理过期Session
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return session;
}

  到此我们明白了 Session 是如何创建出来的,创建出来后 Session 会被保存到一个 ConcurrentHashMap 中。

  可以看 StandardSession 类:


protected Map www.jintianxuesha.com sessiOns= new ConcurrentHashMap<>();

  到这里大家应该对 Session 有简单的了解了。Session 是存储在 Tomcat 的容器中,所以如果后端机器是多台的话,因此多个机器间是无法共享 Session 的。此时可以使用 Spring 提供的分布式 Session 的解决方案,是将 Session 放在了 Redis 中。

 

3. Token

  Session 是将要验证的信息存储在服务端,并以 SessionId 和数据进行对应,SessionId 由客户端存储,在请求时将 SessionId 也带过去,因此实现了状态的对应。

  而 Token 是在服务端将用户信息经过 Base64Url 编码过后传给客户端,每次用户请求的时候都会带上这一段信息,因此服务端拿到此信息进行解密后就知道此用户是谁了,这个方法叫做 JWT(Json Web Token)。

技术分享图片

 

 

  Token 相比较于 Session 的优点在于,当后端系统有多台时,由于是客户端访问时直接带着数据,因此无需做共享数据的操作。

Token 的优点:



  • 简洁:可以通过 URL,POST 参数或者是在 HTTP 头参数发送,因为数据量小,传输速度也很快。



  • 自包含:由于串包含了用户所需要的信息,避免了多次查询数据库。



  • 因为 Token 是以 Json 的形式保存在客户端的,所以 JWT 是跨语言的。



  • 不需要在服务端保存会话信息,特别适用于分布式微服务。



JWT 的结构

  实际的 JWT 大概长下面的这样,它是一个很长的字符串,中间用.分割成三部分。

技术分享图片

 

 

 JWT 是由三部分组成的:


(1)Header

  Header 是一个 Json 对象,描述 JWT 的元数据,通常是下面这样子的:


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

  上面代码中:



  • alg 属性表示签名的算法(algorithm),默认是  HMAC SHA256(写成 HS256)。



  • type 属性表示这个令牌(Token)的类型(type),JWT 令牌统一写为 JWT。最后,将上面的 Json 对象使用 Base64URL 算法转成字符串。



  JWT 作为一个令牌(Token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

(2)Payload

  Payload 部分也是一个 Json 对象,用来存放实际需要传输的数据,JWT 官方规定了下面几个官方的字段供选用:



  • iss (issuer):签发人



  • exp (expiration time):过期时间



  • sub (subject):主题



  • aud (audience):受众



  • nbf (Not Before):生效时间



  • iat (Issued At):签发时间



  • jti (JWT ID):编号



  当然除了官方提供的这几个字段我们也能够自己定义私有字段,下面就是一个例子: 


{
"name": "xiaoMing",
"age": 14
}

  默认情况下 JWT 是不加密的,任何人只要在网上进行 Base64 解码就可以读到信息,所以一般不要将秘密信息放在这个部分。这个 Json 对象也要用 Base64URL 算法转成字符串。

(3)Signature

  Signature 部分是对前面的两部分的数据进行签名,防止数据篡改。首先需要定义一个秘钥,这个秘钥只有服务器才知道,不能泄露给用户,然后使用 Header 中指定的签名算法(默认情况是 HMAC SHA256)。算出签名以后将 Header、Payload、Signature 三部分拼成一个字符串,每个部分用.分割开来,就可以返给用户了。HS256 可以使用单个密钥为给定的数据样本创建签名。当消息与签名一起传输时,接收方可以使用相同的密钥来验证签名是否与消息匹配。

技术分享图片

 

 

 Java 中如何使用 Token

  上面我们介绍了关于 JWT 的一些概念,接下来如何使用呢?首先在项目中引入 Jar 包:


compile(‘io.jsonwebtoken:jjwt:0.9.0‘)

  然后编码如下:


// 签名算法 ,将对token进行签名
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 通过秘钥签名JWT
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("SECRET");
Key signingKey
= new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
Map
claimsMap = new HashMap<>();
claimsMap.put(
"name","xiaoMing");
claimsMap.put(
"age",14);
JwtBuilder builderWithSercet
= Jwts.builder()
.setSubject(
"subject")
.setIssuer(
"issuer")
.addClaims(claimsMap)
.signWith(signatureAlgorithm, signingKey);
System.out.printf(builderWithSercet.compact());

  发现输出的 Token 如下:


eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoiaXNzdWVyIiwibmFtZSI6InhpYW9NaW5nIiwiYWdlIjoxNH0.3KOWQ-oYvBSzslW5vgB1D-JpCwS-HkWGyWdXCP5l3Ko

  此时在网上随便找个 Base64 解码的网站就能将信息解码出来:

技术分享图片

 

 

完结。


推荐阅读
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • Cookie学习小结
    Cookie学习小结 ... [详细]
  • 使用HTML和JavaScript实现视频截图功能
    本文介绍了如何利用HTML和JavaScript实现从远程MP4、本地摄像头及本地上传的MP4文件中截取视频帧,并展示了具体的实现步骤和示例代码。 ... [详细]
  • 为什么多数程序员难以成为架构师?
    探讨80%的程序员为何难以晋升为架构师,涉及技术深度、经验积累和综合能力等方面。本文将详细解析Tomcat的配置和服务组件,帮助读者理解其内部机制。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • 网络爬虫的规范与限制
    本文探讨了网络爬虫引发的问题及其解决方案,重点介绍了Robots协议的作用和使用方法,旨在为网络爬虫的合理使用提供指导。 ... [详细]
  • packagecom.panchan.tsmese.utils;importjava.lang.reflect.ParameterizedType;importjava.lang. ... [详细]
  • 本文介绍了Java编程语言的基础知识,包括其历史背景、主要特性以及如何安装和配置JDK。此外,还详细讲解了如何编写和运行第一个Java程序,并简要介绍了Eclipse集成开发环境的安装和使用。 ... [详细]
  • 本文介绍了一种支付平台异步风控系统的架构模型,旨在为开发类似系统的工程师提供参考。 ... [详细]
  • malloc 是 C 语言中的一个标准库函数,全称为 memory allocation,即动态内存分配。它用于在程序运行时申请一块指定大小的连续内存区域,并返回该区域的起始地址。当无法预先确定内存的具体位置时,可以通过 malloc 动态分配内存。 ... [详细]
  • 本文介绍了多种开源数据库及其核心数据结构和算法,包括MySQL的B+树、MVCC和WAL,MongoDB的tokuDB和cola,boltDB的追加仅树和mmap,levelDB的LSM树,以及内存缓存中的一致性哈希。 ... [详细]
  • 解决SQL Server数据库sa登录名无法连接的问题
    在安装SQL Server数据库后,使用Windows身份验证成功,但使用SQL Server身份验证时遇到问题。本文将介绍如何通过设置sa登录名的密码、启用登录名状态以及开启TCP协议来解决这一问题。 ... [详细]
  • HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送www方式的数据。HTTP协议采用了请求响应模型。客服端向服务器发送一 ... [详细]
  • 本文详细介绍了 HTML 中 a 标签的 href 属性的多种用法,包括实现超链接、锚点以及调用 JavaScript 方法。通过具体的示例和解释,帮助开发者更好地理解和应用这些技术。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
author-avatar
大海2502902497
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有