场景
Http 协议是无状态的,也就是说一个请求过来,服务器不知道这个请求的用户是不是已经登录过了,不知道他的状态,只能再把这个请求重定向到登陆页面,这样用户就疯了,怎么一直让我登录?后来人们发现如果 Http 能够保持一些会话信息,将会变得更加有用,例如登录、购物车等场景。
于是,两种用于保持 HTTP 连接状态的技术就应运而生了:Cookie、Session。
相同点:
- Session 和 Cookie 都是为了让 Http 协议有状态而存在。
- Session 通过 Cookie 工作,Cookie 传输的 SessionID 让 Session 知道这个客户端到底是谁。
区别:
- Cookie 将信息保存在客户端。
- Session 将信息保存到服务器。
举例:有一个店铺发放会员卡,一种是给会员一张卡,上面每消费一次就盖一个戳,这就是客户端保持。另一种是发放的会员卡只有一个卡号,每次店铺店员要根据卡号查一下会员的消费情况,这就是服务端保持状态。
会话
简单的讲如果用户需要登录,那么就可以简单的理解为会话,如果不需要登录,那么就是简单的连接。比如,不同用户将不同商品加入到购物车中, 也就是说必须把每个用户区分开。因为 HTTP 请求是无状态的,所以想出了一个办法,那就是给每个用户配发一个会话标识(Session id),简单的讲就是一个既不会重复,又不容易被找到规律以仿造的随机字符串,使得每个用户的收到的会话标识都不一样, 每次用户从客户端向服务端发起 HTTP 请求的时候,把这个字符串给一并发送过来, 这样服务端就能区分开谁是谁了,至于客户端(浏览器)如何保存这个“身份标识”,一般默认采用 Cookie 的方式,这个会话标识(Session id)会存在客户端的Cookie中。
Cookie
含义:Cookie 是服务器传给客户端并保存在客户端的一小段文本信息,这个 Cookie 是有大小,数量限制的。
属性项 | 介绍 |
---|---|
NAME=VALUE | 键值对,可以设置保存的 Key/Value,这里 NAME 不能和其他属性项名字一样 |
Expires | 过期时间,这个时间点后 Cookie 失效 |
Domain | 生成 Cookie 域名 |
Path | 该 Cookie 是在当前哪个路径下生成的 |
Secure | 加密设置,设置之后只会在 SSH 连接时才会回传该 Cookie |
基本原理:客户端请求服务器,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个Cookie。客户端浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。
Session
含义:Session 是基于 Cookie 来工作的,同一个客户端每次访问服务器时,只要当浏览器在第一次访问服务器时,服务器设置一个 id 并保存一些信息(例如登陆就保存用户信息,视具体情况),并把这个 id 通过 Cookie 存到客户端,客户端每次和服务器交互时只传这个 id,就可以实现维持浏览器和服务器的状态,而这个 id 通常是 NAME 为JSESSIONID 的一个 Cookie。
实际上,有几种方式让 Session 正常工作:
- 通过 URL 传递 SessionID,当浏览器不支持 Cookie 功能时,浏览器会将用户的 SessionCookieName(默认为 JSESSIONID)重写到用户请求的URL参数中。格式:
/path/Servlet;name=value;name2=value2?Name3=value3
- 通过 Cookie 传递 SessionID。
基本原理:当浏览器第一次访问服务器时,服务器创建 Session 并将 SessionID 通过 Cookie 带给浏览器保存在客户端,同时服务器根据业务逻辑保存相应的客户端信息保存在 Session 中;客户端再访问时上传 Cookie,服务器得到 Cookie 后获取里面的 SessionID,来维持状态。
生命周期:当Session 创建后,浏览器关闭,会话级别的 Cookie 被销毁,如果没有超过设定时间,该 SessionID对应的 Session 是没有被销毁的。
Session 存储和运维:现在后端服务都是分布式部署,Session 一般统一放在 redis 集群中。这样有个问题就是一旦 redis 故障,可能会影响所有的用户请求。所以,在后台进行 Session 的存储和运维这件事是非常重要和危险的,对可靠性的要求非常高。
那么,存在两个问题:
- 怎么确保安全性?
- 如何能不存储 Session 呢?
基于 Token 的身份验证
工作原理:通过对数据做签名 Sign 保障数据安全性, 比如说服务端用 HMAC-SHA256 加密算法,再加上一个只有服务端才知道的密钥, 对数据做一个签名, 把这个签名和数据一起作为 Token 发给客户端, 客户端收到 Token 以后可以把它存储起来,比如存储在 Cookie 里或者 Local Storage 中,由于密钥除了服务端任何其他用户都不知道, 就无法伪造令牌 Token。如此一来,服务端就不需要保存 Token 了, 当客户端把这个 Token 发给服务端时,服务端使用相同的 HMAC-SHA256 算法和相同的密钥,对数据再计算一次签名, 和 Token 中的签名做个对比, 如果相同,说明已经登录过了, 即验证成功,若不相同, 那么说明这个请求是伪造的。这也就实现了时间换取空间(CPU 计算时间换取 Session 存储空间)。
JWT:JSON Web Token,是 Token 的一种实现方式,并且基本是 Java Web 领域的事实标准。由 3 部分构成:
- Header :描述 JWT 的元数据。定义了生成签名的算法以及 Token 的类型。
- Payload(负载):用来存放实际需要传递的数据。
- Signature(签名):服务器通过 Payload、Header 和一个密钥使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
1 | x_jwt=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiMDE1MTA4ODYiLCJuYW1lIjoi6aW26L6JIn0sImF1ZCI6Ingtb2F1dGgiLCJleHAiOjE1ODQ1OTM4NDAsImlhdCI6MTU4NDU3OTQ0MCwiaXNzIjoieC1vYXV0aCIsInN1YiI6IjAxNTEwODg2In0.9_veR_BC6WYfdeLyDumeRqqgq4yoA7DOHYySZDMXGOK-5BGRBQK9fcaEheAvwePYHOgIzshaImwne0pJjAPVqg; |
工作流程:在基于 Token 进行身份验证的的应用程序中,用户登录时,服务器通过 Payload、Header和一个密钥(secret)创建令牌(Token)并将 Token 发送给客户端,然后客户端将 Token 保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP Header 的 Authorization 字段中:Authorization: 你的 Token。
Oauth 2.0
Oauth 2.0 是一种授权机制,用来授权第三方应用,获取用户数据,它与 JWT 其实并不是一个层面的东西。