热门标签 | 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 解码的网站就能将信息解码出来:

技术分享图片

 

 

完结。


推荐阅读
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
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社区 版权所有