这三者本质上不是同一个东西,但是它们都和维持状态信息有关。我们登陆一个账户以后,后续很长时间都不用再重新登录了,这是为什么呢?熟悉HTTP
的都知道,HTTP
是无状态的,也就是说这次你访问了服务器,然后关闭网页,然后你再次访问服务器,这时候服务器是不知道又是你来访问的,但是登录状态是保持的,这里就会用到上面所说的三个东西。
1.什么是状态信息?
当我们在浏览器网站登陆一个账号以后,如果我们希望访问后续页面都维持登录的状态,就需要用到cookie
,session
,token
来进行实现。如果说没有使用上面任何一个技术,我们要维持用户的登录状态信息,是非常麻烦的,比如说发起一个登录的请求,把用户密码传到服务端,服务端进行验证,如果正确的话就要把用户名和密码再返回给前端,以维持登录的状态信息,那么后续请求的所有页面我们都需要带上用户名和密码。

这样做的话毫无疑问既不安全又不方便,所以我们就可以使用上面三种方法解决。
2.cookie
使用cookie
技术的话,前端发起登录请求,把用户名密码发送给服务端进行验证,如果通过的话,服务端会进行cookie
设置,也就是set-cookie
,cookie
里会有用户的一些信息比如id
,name
等等,然后响应到浏览器,浏览器会把这些信息都保存在cookie
上,保存以后呢访问后续页面都会自动带上cookie
,这样呢就维持了页面的登录状态。如果不设置有效期的话,默认是关闭浏览器以后cookie
就会失效,再次打开就要重新登录。



cookie
的优点呢就是能够维持用户的登录状态。
那如果只用cookie
来存储状态信息呢其实是会有很多问题的。
首先是会有安全的风险,因为cookie
是存储在客户端的,所以用户是可以去篡改cookie
的,对于服务器来说,它只会根据请求的cookie
信息去获取当前登录的用户,所以一旦被篡改的话就不能保证安全性。

我们可以用数据签名的方式来实现,我们把所有数据链接起来拼接存在服务器里的密令,取Hash
值作为签名,签名随同数据一起发送给浏览器作为cookie
,这样后续服务端就可以通过验证签名的方式来判断数据是否被篡改。

其次就是cookie
的容量很小只有4kb,而且只能存储一个字符串,存储不了多少信息,最后就是用户其实是可以通过浏览器禁用cookie
的,所以我们不能完全依赖cookie
保持登录状态。
3.session
所以就有了session
,也就是会话。浏览器访问服务端就是会话的开始,比较模糊的是会话的结束的时间,因为你关掉网页也有可能只是点错了而已,因此不同的网站对于用户的会话都设置了时间以及唯一ID,这里的ID就是通常所说的sessionid
,这里的时间就是结束会话的时间。因为是服务端自己定义的东西,所以一般都会保存在服务端的数据库。那为什么有了用户名还要有这个sessionid
?
使用session
技术的话,前端发起登录请求,把用户名密码发送给服务器进行验证,如果通过的话,服务端就会保存用户信息比如用户名,会话关闭的时间也就是cookie
的有效期,并生成一个key
用于数据关联,这个key
就是sessionid
,这一步和cookie
很像。响应给前端的时候会在响应头上添加一个set-cookie
的属性,值就是sessionid
,前端会自动在cookie
中存入当前的sessionid
,在后续请求页面的时候就会在请求头上添加cookie
,把sessionid
给传入到服务端,服务端拿到以后会在保存的用户信息里通过key
也就是sessionid
找到对应的登录用户信息以维持登录状态,如果超过了有效期就会提示重新登录。


session
的使用是非常方便的,请求登录到服务器成功以后存入用户信息时,不仅仅可以存入一个字符串,甚至可以把整个用户信息都存入进去,下次获取的时候是非常方便的。而且安全性高,因为用户信息都是存储在服务端,而且存储的内容也大大增加可以存储整个user对象。
同样session
也是有弊端的,因为存储的容量大,在高并发的情况下是会比较占用服务器内存的,而且扩展性比较差以及依然需要依赖cookie
这就导致会有跨域问题。
扩展性比较差的意思就是,比如说后端服务器压力比较大,就有可能对它进行集群部署,比如我们前端发起登录请求,服务端往session
里存入用户登录信息只会存入到一台服务器,其他的服务器并不会有用户的登录信息。集群之后负载均衡就有可能浏览器的下一次请求就会请求到别的服务器上面,就会判定用户当前未登录。所以在集群的情况下,session
是不支持的。

以及现在都是前后端分离的架构方式,在移动互联网盛行的情况下还会有移动端H5,小程序,安卓端,苹果端,他们都会有各自的端口域名,这时候前端再请求后端就会发生跨域,跨域的情况下cookie
是默认无法传递的,需要在后端设置允许跨域,还需要在前端单独的设置允许跨域的cookie
传递,这是比较麻烦的,所以在集群环境以及前后端分离的架构下session
已经不再适用。
session
的本质还是cookie
,区别就是服务端只给浏览器一个sessionid
用于身份区分,用户信息是保存在服务端的。
4.token
前面说了在移动互联网盛行的情况下还会有移动端H5,小程序,安卓端,苹果端,这些客户端的网络请求接口默认是没有cookie
机制的,那怎么办呢?客户端程序将sessionid
在存储系统里面保存起来,然后sessionid
换个名字叫token
,这就出来token
了。token
和session
还是一样的,只是少了浏览器的cookie
机制,需要自己维护。客户端为了遵循Bearer
认证规范,请求的时候就不再使用Cookie
字段(就是前面浏览器发送请求给客户端时请求头自动携带的属性),而是Authorization
字段。到这里会发现token
和session
的用户数据都是保存在服务端的,在单个服务器下没有问题,如果出现上面所说的集群部署的情况,就会出现某个服务器没有用户数据的情况导致需要重新登录,通常解决这种问题需要服务端再架设一个中心化的存储服务,比如Redis
来专门存储用户信息,但是这样也会有问题,如果中心化的服务器出现故障,就会导致所有服务器都连带故障,另外在一些边缘计算平台里也没法使用中心化的服务,所以还是希望用户数据由客户端保管。其实也好办,直接把cookie
的数据搬过来作为token
就行了,但是前面关于cookie
容易被篡改的解决方案是随便想的就会有漏洞,而且对于每个人的算法都不一样(Hash算法),会造成五花八门不方便协作,所有我们就需要一个标准,这个标准就是JWT
。
JWT
全称为json Web token
,其实就是通过json
来进行传递的加密后的字符串。下面这一段就是通过JWT
生成出来的加密以后的token
字符串。

它里面由三段信息组成,其中红色部分是header
包含了Hash算法
和token类型(加密类型)
。其中算法是不可逆的,所以保证了安全性,将header
的json
数据进行base64
编码以后就得到了上面的红色信息。

紫色部分是payload
负载数据,也就是我们需要传递的数据,这里面存放的就是要存储的信息,比如用户名,用户密码等等用户的信息。通用的这一段json
通过base64
编码以后就会变成上面的紫色信息。如果我们前端想要获取用户的信息就可以截取紫色信息出来然后通过base64
解码,这就可以获取到了。

蓝色部分就是JWT
的安全所在了,它是signature
,我们称之为密钥,它里面呢会将base64
编码后的header
和payload
相加,然后再加上只有自己知道的一个私钥,最后再通过header
当中指定的算法来进行加密,就得到了蓝色的这一段。

接着来讲一下JWT
的使用过程,同样是前端发生登录请求,服务端进行验证,通过以后会创建一个jwt
字符串,它会包含三段信息,就是上面那三段信息,然后把创建好的jwt
字符串通过响应头的token
属性返回给前端,如果前端想要获取到用户的信息,就分割jwt
的字符串,前面讲了第二段信息就是保存的用户载体,里面就有用户信息,拿到以后通过base64
进行解码即可。登录成功以后后续的请求只需要在请求头当中设置一个跟后端约定的属性,然后再把jwt
字符串放进去,后端拿到jwt
字符串以后会进行解密,然后会验证这个signature
,如果验证成功,信息没有被篡改,就会进行放行,就能保持一个安全的登录状态。

所以使用JWT
不需要使用后端的存储,也不需要使用前端的存储,他就是一个加密后的字符串而已,对于现在的集群环境,以及前后端分离的架构,都是可以方便使用的。
token
虽然被加密但是其实也是能够被解开的,所以最好不要把敏感信息存入到token
里面。