Cookie 与 Session
在我们访问页面时,通常是通过 HTTP 协议的,该协议的特点之一是无状态。当我们通过HTTP协议去访问某一个网站,此时就会向网站服务端发送请求。HTTP 协议确保每一个请求都是独立的,于是服务端无法确定当前请求的身份,而 Cookie 和 Session可以解决这个问题。
1.1 Cookie
Cookie 存储在客户端,比如访问网站时的浏览器,每一次请求,Cookie都会自动的发送给服务端,当然这是可以手动禁用的。
Cookie 主要的属性
key=value: 键值对,必须都为字符串类型。值为 Unicode字符时,使用字符编码。值为二进制数据时,使用 Base64编码
domain: 指定 cookie 所属的域名,默认是当前域名
path: 指定 cookie 在哪个路径(即路由)下生效,默认为 ‘/’。若指定为/user,那么只有 /user 下的路由可以访问到该 cookie,如:/user/find
maxAge: cookie 失效时间,单位:秒, maxAge < 0 则关闭浏览器就失效,maxAge = 0 ,删除,默认情况maxAge = -1
expires: cookie 过期时间,在设置的某个时间后 该 cookie失效。没有 maxAge 好用。
secure: true / false,表示 cookie 是否使用安全协议(如 HTTPS,SSL)传输,默认为 false。设置为 ture 时, cookie 在HTTP传输中无效,而在 HTTPS 中会有效。
httpOnly: 禁止通过 JS脚本获取 cookie,但可以通过浏览器调试工具获取到 Application 里的 cookie,能防一定的XSS攻击
1.2 Session
Session 是另一种记录服务端和客户端会话状态的机制,Session 是基于 Cookie 实现的,Session 存储在服务端,当发送请求后,服务端会存储当前请求的sessionId (区分不同的客户端)到 客户端的 Cookie 中。
安全性: Cookie 不安全,存储在客户端,可自行修改
Session 安全,存储在服务端
存储类型 :Cookie key为字符串value也为字符串
Session key为字符串,value 可为能序列化的任何数据
有效期 :Cookie 长时间保持
Session 时间较短,默认为30秒,且在客户端关闭就失效。
存储量 :单个Cookie不能超过4K,即4096 Byte
Session 没有固定限制,存储在内存,但是过大会占资源。
2.1 Cookie
使用 Cookie 需注意的问题:
存储在客户端,容易被篡改,使用前需验证是否合法
尽量不存敏感数据,如用户密码,账号余额
使用 httpOnly ,提高安全性
存储的数据量不能过大,单个Cookie最多只能存储 4kb 的数据
设置正确的 domain 域 和 path,尽量减少数据传输
无法跨域
浏览器对单个网站最多存 20个 Cookie,而浏览器一般指允许存放 300个Cookie
移动端不适合用 Cookie ,而 Session 又是基于 Cookie 实现,故移动端常用 Token
使用 Cookie 存储,需要设置有效时间,这会影响 JWT 的时间设置,比如当 JWT 有效时间为 1天,那么 Cookie 的有效时间必须大于或等于 1天。
2.2 Session
使用 Session 需注意的问题:
当用户在线过多时,会占据更多内存,需在服务端定期处理过期的 Session
集群环境下,服务端需解决多台 web 服务器共享session的问题,否则同一个用户无法访问服务端不同的web服务器。
共享session 会遇到跨域问题,服务端需在各个应用间解决 cookie 跨域 问题
sessionId 存储在客户端的 cookie中,如果浏览器禁止 cookie 或不支持 cookie,可以把 sessionId 写在 URL 参数后面,即 session不一定必须基于 cookie 实现
2.3 Token
使用 Token 需注意的问题:
数据库存储 Token,若导致查询时间长,可选择存放在内存,比如使用 Redis 数据库
Token 由应用管理,避免了同源策略的限制。
Token 不依赖 Cookie, 可避免 CSRF(跨站域请求伪造)攻击
2.4 JWT
使用 JWT 需注意的问题:
JWT 不依赖 Cookie,,支持 CORS 跨域资源共享
JWT 默认不加密,不过可设置加密,生成原始Toekn后,再用指定的加密算法加密一次
JWT 不加密时不能存放敏感数据,否则有安全隐患
JWT 除了认证以外,还可以用来交换信息,降低了服务端的数据库交互
JWT 最大优势是服务端无需依赖 Session,方便服务端的认证与授权业务的拓展,同时也有一定局限,无法在使用过程中废弃某个 Token 或 更改 Token 的权限,除非使用更多的逻辑进行处理
JWT 本身包含了认证信息,故最好将JWT有效期设置短一些,对于比较重要的权限,使用时应再次验证用户身份
JWT 适合一次性的命令认证,颁发有效期很短的JWT,降低泄露的危险
为了降低盗用概率,JWT 应使用 HTTPS 安全协议传输。



JWT 认证流程:
用户携带个人的用户名和密码发送请求,服务端认证成功后,返回客户端一个JWT字符串
客户端将 Token 保存到本地(浏览器的话通常是 localStorage 或 Cookie)
当用户要访问受保护的路由或资源时,需设置 Header 请求头的 Authorization 的值,使用 Bearer 模式 添加 JWT 字符串,比如 Authorization: Bearer <token>
服务端的保护路由会检查 Header 种 Authorization 中的 JWT 信息,若合法则运行用户行为。
使用 JWT 的特点:
内部包含一些会话信息,除了验证、签名生成 JWT外,其他情况无需数据库交互
JWT 并不一定依赖于 Cookie,所以可以跨域访问,即支持向第三方应用进行认证与授权
由于用户状态并不存储在服务端的内存,故是一种无状态的认证机制
3.2 JWT 与 Token 相比
JWT 是 Tokne 的拓展,它们之间的 相同点 有:
都是访问资源的令牌
都可以记录用户信息
都使服务端无状态化
都必须通过验证,客户端才能访问服务端受保护的资源
不同点:
Token,服务端 必须 查询数据库获取用户信息,然后再验证 Token 是否有效,
JWT 是将 Token 和 Payload 加密存储在客户端,服务端只需使用密钥解密进行验证,无需查询数据库
3.3 JWT的主要组成
JWT 主要由三部分组成,分别是 header(请求头)、payload(有效负载)、signature(签名),JWT由这三个字符串以句点 . 的方式连接,JWT 通常显示为:xxx.yyy.zzz,即 Header.Payload.Signature
3.3.1 Header
Header 标头通常由两部分组成:
令牌的类型(即 JWT )
签名算法
如 HMAC SHA256 或 RSA,它会用 Base64 编码组成 JWT 结构的第一部分
注:Base64 是一种编码,即是可以进行解码的,并不是一种加密过程。
3.3.2 Payload
令牌的第二部分是 Payload (有效负载),主要存储一些键值对
和 Header 部分一样,Payload 会采用 Base64 编码,不过是作为 JWT 结构的第二部分
3.3.3 Signature
前面两部分都是用 Base64 进行编码的,即前端后端都可以解析里面的信息,Signature 签名需要使用编码后的header 和 payload 以及我们提供的一个字符串密钥,然后使用 header 中指定的签名算法,例如 HS256 进行签名。签名的作用是保证 JWT 没有被篡改过。
Base64 是一种编码方式,是可逆的,那么信息就容易暴露。在JWT中,不应该在 payload 负载里加入任何 敏感 的数据,例如用户的个人密码,不适合放到 JWT。
JWT 经常用于设计用户认证和授权系统,或者实现 Web 应用的单点登录。
Header、Payload 和 Signature,前两个是基于 Base64 编码的,而签名 SIgnature 则是由服务端来决定,通过【加密算法】 + 【密钥】规定一种加密机制,从而确定签名。
签名的密钥是固定的,只要不泄露,密码就不会出现问题,当然可以设置的更复杂一些,比如拿用户的用户名进行加密,然后再次使用加密算法进行签名,即二次加密,最后将加密后的用户名存储到 Payload里,这样一来每次 JWT 验证,既不用查询数据库,也不用担心各用户的签名都一样。