🌟个人主页:时间会证明一切.
会话技术
会话:一次会话中包含多次请求和响应。
- 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
- 功能:在一次会话的范围内的多次请求间,共享数据
- 方式:
- 客户端会话技术:Cookie
- 服务器端会话技术:Session
Cookie
Cookie:客户端会话技术,将数据保存到客户端,以后每次请求都携带 Cookie 数据进行访问,基于HTTP协议实现
当用户第一次访问并登陆一个网站的时候,Cookie的设置以及发送会经历以下4个阶段:
- 客户端发送一个请求到服务器
- 服务器返回一个 httpResponse 响应到客户端,其中包含 Set-Cookie 的响应头
- 客户端收到服务器返回的 Set-Cookie 响应头里的数据,将其保存 Cookie,之后向服务器发送请求时,httpRequest 请求中会包含一个 Cookie 的请求头
- 服务器可以通过检查该 Cookie 获取信息,返回响应数据
服务器向客户端发送 Cookie
Cookie 基本使用
-
创建 Cookie 对象,设置数据
Cookie cookie = new Cookie("key","value");
-
发送 Cookie 到客户端 使用 response 对象
response.addCookie(cookie)
Set-Cookie
是通过HTTP响应头部的Set-Cookie
字段来设置的。当服务器要向客户端设置一个新的Cookie时,会在HTTP响应中包含这个Set-Cookie
字段。
例如,下面是一个包含Set-Cookie
头部的HTTP响应示例:
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: key=value
在这个示例中,服务器通过Set-Cookie
头部将一个名为key
的Cookie发送给客户端。该Cookie的值为value
当客户端收到这个Set-Cookie
头部时,会解析其中的信息,并存储这个Cookie。在后续的请求中,客户端会将这个Cookie自动包含在请求头部的Cookie
字段中发送给服务器。服务器接收到请求后,可以根据这个Cookie的值进行相应的处理。
客户端携带 Cookie 向服务器发送请求
在HTTP请求中,Cookie是通过包含在请求头部的"Cookie"字段中发送的。具体来说,Cookie会以键值对的形式表示,多个键值对之间使用分号(;)进行分隔。例如:
GET /example HTTP/1.1
Host: example.com
Cookie: key1=value1; key2=value2; key3=value3
在这个示例中,请求头部的"Cookie"字段携带了三个Cookie,即key1=value1、key2=value2和key3=value3。
服务器收到请求后,可以解析请求头部中的"Cookie"字段,以获取客户端发送的Cookie数据,并根据需要进行处理或响应
Cookie 使用细节
-
Cookie存活时间
- 默认情况下,Cookie 存储在浏览器内存中,当浏览器关闭,内存释放,则 Cookie 被销毁
- setMaxAge(int seconds):设置 Cookie 存活时间
- 正数:将 Cookie 写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除
- 负数:默认值,Cookie 在当前浏览器内存中,当浏览器关闭,则 Cookie 被销毁
- 零: 删除对应 Cookie
-
Cookie 存储中文
- Cookie不能直接存储中文 在tomcat 8 之前 cookie中不能直接存储中文数据
- 在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
- 如需要存储,则需要进行转码:URL编码
使用 Cookie 时存在的问题
- 不安全,不要存储敏感数据,比如用户密码,账户余额,因为存储在客户端,容易被客户端篡改
- 能存储的数据量不能超过 4KB;
- 有数量限制,一个浏览器针对一个网站最多存 20 个 Cookie,浏览器一般只允许存放 300 个 Cookie;
- 移动端对 Cookie 的支持不是很好,而 Session 需要基于 Cookie 实现,所以移动端常用的是 token;
- Cookie 为不可跨域的:每个 Cookie 都会绑定单一的域名,无法在别的域名下获取使用;
跨域问题
跨域的由来不得不提到 XSS 攻击。
全名:Cross-site scripting(跨站脚本攻击)。这是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。若受害者运行这些恶意代码,攻击者就可以突破网站的访问限制并冒充受害者。简单地说,就是我可以在你的网站偷偷上运行我的代码,那有多危险。
为了应对这种情况,便有了浏览器的同源策略。
同源策源
这个策略是由浏览器去实现的,其目的在于限制请求方如何与另一个源的资源进行交互。
“源”你就理解为地址,但它的完整格式由协议、域名、端口组成,如:
https://store.company.com:8000
另一个客户端请求上面的地址,如果协议、域名、端口其中有一个不同,这两个就算两个源,客户端的行为要受到限制。
下表给出了与 URL http://store.company.com/dir/page.html
的源进行对比的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html |
同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html |
同源 | 只有路径不同 |
https://store.company.com/secure.html |
失败 | 协议不同 |
http://store.company.com:81/dir/etc.html |
失败 | 端口不同 ( http:// 默认端口是 80) |
http://news.company.com/dir/other.html |
失败 | 主机不同 |
不同源的请求,就被认为是跨域,是不被允许的。
Cookie跨域问题
Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie。
正常情况下,同一个以及域名下的两个二级域名也不能交互Cookie,比如test1.jianshu.com与test2.jianshu.com,因为二者简书的域名不完全相同,如果想要jianshu.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数为jianshu.com,这样使用test1.jianshu.com和test2.jianshu.com就能访问同一个域名了。
设置的过程,服务器设置cookie的时候,需要指定cookie的domain,当domain与当前host的匹配不上的时候,responseHeader里的set-cookie不会设置成功。这也就是cookie不支持跨域问题。
Session
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中
-
session 是另一种记录服务器和客户端会话状态的机制。
-
session 是基于 cookie 实现的
-
session 存储在服务器端,可以理解为一个状态列表,他拥有一个唯一识别符号JSESSIONID,通常存放于 cookie 中
-
在一次会话的多次请求间获取的session是同一个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXOSt1SF-1688566791979)(Cookie、Session 和 Token.assets/image-20230703141620004.png)]
Session 使用流程
- 首先,客户端会发送一个http请求到服务器端。
- 服务端接收请求后,创建对应的 session ,而每一个session是有一个唯一标识id的,tomcat自动将该session的id当作一个Cookie并发送一个http响应到客户端,这个响应头,其中就包含 Set-Cookie 头部。该头部包含了key = JSESSIONID,值=session唯一标识。比如:set-cookie: JSESSIONID=10(每个session的唯一标识)
- 浏览器接收到服务端返回的 JSESSIONID=10后,将此信息存入 Cookie 中,同时 Cookie 记录此 JSESSIONID 属于哪个域名。
- 当用户第二次发起请求时,请求会自动判断此域名下是否存在 Cookie 信息,如果存在浏览器会自动在请求头中添加 Cookie 发送到服务端
- 服务端接收请求,会从 Cookie 中获取 JSESSIONID,再根据 JSESSIONID查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
Session 使用细节
Session 钝化、活化
- 服务器重启后,Session 中的数据是否还在?
- 钝化:在服务器正常关闭后,Tomcat 会自动将 Session 数据写入硬盘的文件中
- 活化:再次启动服务器后,从文件中加载数据到 Session 中
Session销毁:
-
默认情况下,无操作,30分钟自动销毁
<session-config> <session-timeout>30</session-timeout> </session-config>
-
调用 Session 对象的
invalidate()
方法,Session 自动销毁
Cookie 和 Session 区别
- Cookie 和 Session都是来完成一次会话内多次请求间数据共享的
- 区别:
- 存储位置:Cookie是将数据存储在客户端,Session将数据存储在服务端
- 安全性:Cookie不安全,Session安全
- 数据大小:Cookie最大4KB,Session无大小限制
- 存储时间:Cookie可以长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
Base64算法
Base64是什么?
Base64 是一种二进制到文本的编码方式。如果要更具体一点的话,可以认为它是一种将 byte
数组编码为字符串的方法,而且编码出的字符串只包含 ASCII 基础字符。
例如字符串ShuSheng007
对应的 Base64 为U2h1U2hlbmcwMDc=
。其中那个=
比较特殊,是填充符,一会再说。
值得注意的是 Base64 不是加密算法,其仅仅是一种编码方式,算法也是公开的,所以不能依赖它进行加密。
为什么叫Base64?
因为它是基于(Base)64个字符的一种编码方式。使用其编码后的文本只包含64个ASCII码字符(偶尔加一个填充字符=
),如下所示:
Base64使用到的64个 字符:
A-Z
26个a-z
26个0-9
10个+
1个/
1个
下图是Base64码表,可以看到从0到63的每个数字都对应一个上面的一个字符。

Base64解决什么问题?
为什么各系统以及传输协议中二进制不兼容
导致各系统和传输协议中二进制不兼容的原因可能有以下几点:
-
数据表示方式:不同系统或协议可能使用不同的数据表示方式,例如大端字节序和小端字节序的区别。如果在数据交换过程中没有正确处理字节序,就会导致数据解析错误。
-
数据类型和长度:不同系统或协议可能对数据类型和长度的定义存在差异。例如,一个系统使用32位整数表示某个字段,而另一个系统使用64位整数表示相同的字段。在数据交换时,如果没有对数据类型和长度进行适当的转换和匹配,就会导致数据解析错误或数据截断。
-
数据编码方式:不同系统或协议可能使用不同的数据编码方式,如ASCII、UTF-8、UTF-16等。如果在数据交换时没有正确指定和处理编码方式,就会导致数据解析错误或乱码。
-
数据格式和协议规范:不同系统或协议可能使用不同的数据格式和协议规范。例如,一个系统使用JSON格式进行数据交换,而另一个系统使用XML格式。如果在数据交换时没有按照正确的格式和规范进行解析和处理,就会导致数据解析错误或通信失败。
-
加密和压缩算法:不同系统或协议可能使用不同的加密和压缩算法。如果在数据交换时没有正确处理加密和压缩算法,就无法正确解析和还原数据。
为了解决这些二进制不兼容性问题,通常需要进行数据格式转换、字节序转换、数据类型转换、编码转换等操作,以确保数据在不同系统和协议之间正确解析和传输。
Base64 就是为了解决各系统以及传输协议中二进制不兼容的问题而生的
为啥 Base64 大家就兼容了呢?因为 Base64 满足了各方的需求,各方说了,我们只保证支持 ASIIC 中那些基础字符,其他的我们不能保证,于是 Base64 就去从那些基础字符里挑了64个,所以大家都高兴了。
Base64编码是一种将二进制数据转换为可打印字符的编码方式。它通过将原始二进制数据转换为由64个字符组成的可打印字符集,从而解决了各系统和传输协议中二进制不兼容的问题。以下是Base64可以解决二进制不兼容性的原因:
-
可打印字符集:Base64编码使用64个可打印字符(A-Z,a-z,0-9和"+“,”/")表示二进制数据,这些字符在大多数系统和协议中都是兼容的。通过将二进制数据转换为可打印字符集,可以确保数据能够在不同系统和协议之间正确解析和传输。
-
字符集一致性:Base64编码使用的字符集在大多数系统中是标准的,无论是ASCII字符集还是Unicode字符集。这种一致性确保了数据在不同系统和协议中的兼容性。
-
无格式限制:Base64编码只关注数据的二进制表示,而不关注数据的具体格式。这意味着可以对任意类型的二进制数据进行Base64编码,无论是图像、音频、文本还是其他类型的数据。因此,通过Base64编码,可以将任何类型的二进制数据转换为字符串形式,以便在各种系统和协议之间传输。
需要注意的是,Base64编码会导致数据稍微增加约33%的大小,因为每3个字节的二进制数据会编码成4个字符。因此,在使用Base64编码传输数据时,需要考虑到数据大小的增加。另外,Base64编码只是解决二进制数据在传输和解析过程中的兼容性问题,而不会解决二进制数据在语义和语法上的兼容性。对于更复杂的数据兼容性问题,可能需要进行更多的数据转换和处理操作。
使用场景
- 证书
- 电子邮件的附件,因为附件往往有不可见字符
- xml 中如果像嵌入另外一个 xml 文件,直接嵌入,往往 xml 标签就乱套了, 不容易解析,因此,需要把 xml 编译成字节数组的字符串,编译成可见字符。
- 网页中的一些小图片,可以直接以 Base64 编码的方式嵌入,不用再链接请求消耗网络资源。
- 较老的纯文本协议 SMTP ,这些文本偶尔传输一个文件时,需要用 Base64
Base64算法
-
Base64算法是一种将二进制数据转换为可打印字符的编码算法。下面是Base64编码的基本步骤:
我们将使用二进制数据 “Hello World!” 进行编码。
- 将 “Hello World!” 转换为对应的ASCII码:
- ASCII码:72 101 108 108 111 32 87 111 114 108 100 33
- 将ASCII码转换为二进制:
- 01001000 01100101 01101100 01101100 01101111 00100000 01010111 01101111 01110010 01101100 01100100 00100001
- 将二进制数据按照每3个字节分组:
- 01001000 01100101 01101100 01101100 01101111 00100000 01010111 01101111 01110010 01101100 01100100 00100001
- 将每个字节转换为8位二进制数,并拼接在一起:
- 010010000110010101101100 011011000110111100100000 010101110110111101110010 011011000110010000100001
- 将24位的二进制数划分为4个6位的小组:
- 010010 000110 010101 101100 011011 000110 111100 100000 010101 110110 111101 110010 011011 000110 010000 100001
- 将每个6位的二进制数转换为十进制数,对应于Base64字符集中的索引:
- 18 6 21 44 27 6 60 32 21 54 61 50 27 6 16 33
- 使用Base64字符集中对应索引的字符替代每个6位二进制数:
- S G V k b G 8 g V u Z + y G B Q
- 得到最终的Base64编码字符串:
- “SGVsbG8gV29ybGQh”
所以,将二进制数据 “Hello World!” 使用Base64编码后得到的Base64编码字符串是 “SGVsbG8gV29ybGQh”。
解码时,可以按照相反的步骤进行操作:
-
将Base64编码字符串按照每4个字符进行分组。
-
将每个Base64字符转换为对应的6位二进制数。
-
将这4个6位的二进制数拼接在一起,得到一个24位的二进制数。
-
将这个24位的二进制数划分为3个8位的字节。
-
将每个8位二进制数转换为十进制数,得到原始的二进制数据。
-
如果Base64编码字符串中存在填充字符’=',则去除填充字符。
需要注意的是,Base64编码是一种用于数据传输和存储的编码方式,并不是加密算法。它可以将二进制数据转换为可打印字符,但不能提供数据的保密性。
- 将 “Hello World!” 转换为对应的ASCII码:
下图是维基百科上面的一个例子
假如我们的原文为Man
,那么下图演示了如何按照上面的步骤将其编码为Base64字符串
可以发现Man
对应的Base64为TWFu
.现在大家应该明白为什么只有64个字符了吧?因为算法将将8bit分割成6bit了,而6bit的取值范围为0~63
。
Base64字符串末尾的=
是什么
有时我们会在Base64字符末尾会看到=
,有时1个,有时2个,这是为啥?
通过上面的我们知道了Base64编码过程是3个字符一组的进行,如果原文长度不是3的倍数怎么办呢? 例如我们的原文为Ma
,它不够3个,那么只能在编码后的字符串中补=
了。缺一个字符补一个,缺两个补两个即可,所以有时候你会看见base64字符串结尾有1个或者2个=
。
Base64 DataURI格式
有时你会发现web页面传给你的base64字符串前面有类似下面的东东。
data:image/jpeg;base64, /9j/4AA...
这是DataURI,大部分浏览器支持直接打开这类二进制数据,但是我们要格外注意,如果你只是想要真实的Base64内容就需要取,
后边的内容
Token
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理和用法。
一、跨域认证的问题
互联网服务离不开用户认证。一般流程是下面这样。
- 用户向服务器发送用户名和密码。
- 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
- 服务器向用户返回一个 session_id,写入用户的 Cookie。
- 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
- 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。
存在的问题
-
扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
-
举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?
解决方案
-
一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。
-
另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
二、JWT 的原理
JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。
{
"姓名": "张三",
"角色": "管理员",
"到期时间": "2023年7月1日0点0分"
}
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。
服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
三、JWT 的数据结构
实际的 JWT 大概就像下面这样。
它是一个很长的字符串,中间用点(.
)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。
JWT 的三个部分依次如下。
- Header(头部)
- Payload(负载)
- Signature(签名)
写成一行,就是下面的样子。
Header.Payload.Signature
下面依次介绍这三个部分。
3.1 Header
Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。
{
"alg": "HS256",