Session、Cookie、Token、单点登录
HTTP无状态
HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。
所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。
Cookie
- cookie可以理解成存储在浏览器 上的一种数据
- 数据格式都是key-value键值对,数据本质上是保存在终端某个文件中。
- cookie不能跨域,浏览器在访问某网站的地址时,如果该网站有cookie保存在浏览器中,可以携带上对应的cookie。
- cookie长度不超过4kb,否则会被截掉。
cookie认证流程图
cookie认证登录超时一般都是在服务端设置cookie有效时间。
cookie认证特点最大的就是用户信息保存在浏览器。
缺点
- 一旦请求被拦截,拿到了cookie后用户信息就泄露了。
- 如果浏览禁止开启cookie,那么就登录不了系统了。
第一点是cookie的严重问题。
Session认证
- session是存储在web服务端容器里。
- 存储的数据格式也是键值对。
- 当访问服务器某个网页的时候,会在服务端开辟一个内存,这块内存为session,而这个内存是跟浏览器关联在一起的。浏览器指的是浏览器窗口或者子窗口。 只允许当前这个session对应的浏览器访问。就算是新开启的浏览器也无法访问。另外一台浏览器也需要记录session的话,就会在创建一个属于自己浏览器的session。
过程
- 浏览器第一次访问服务器的时候会在服务端生成Session,有一个session和它对应的sessionid的生成方式:随机数+时间戳; session可以放在内存中,也可以持久化到数据库和文件中;
- 服务端将sessionID通过cookie形式发送给客户端。session的session的销毁只能通过invalidate和超时。关闭浏览器不会关闭session,因为session存放在服务器。
Java使用HTTPServletRequest的getSession方法创建的。在创建Session的同时、服务器会为该Session生产唯一的SessionID - 这个sessionID在随后的请求汇总会被用来获得已经创建的session。
在Session创建之后、就可以使用session的相关方法往session中增加内容了。而这些内容只会保存在服务器中,发送到客户端的只有sessionID,当客户端再次发送请求的时候,会将这个sessionID带上。服务器再收到请求之后就会依据sessionID找到session,再次使用。
session认证的流程图
- session认证与cookie认证最大的区别就是用户信息存放在服务端的。浏览器有的只是session对应的sessionid
- session认证还是依赖于cookie机制的,所以除了比cookie安全点外,cookie认证的其他缺点session几乎也都有,比如浏览器设置了不存储cookie就登录不了了,还有一次请求携带多余cookie等问题。
- session认证还有一个致命的缺点,由于session是占用了服务器内存,随着系统登录用户越来越多,宝贵的内存资源就被会占用很多,这是最大的缺陷,但这一点一般在用户不多的中小Web应用中不用理会。
- session认证在集群环境中也是有问题的,比如第一次访问是路由到了节点A然后登陆了,session创建在了节点A上,第二次访问路由到了节点B,虽然通过nginx的反向代理可以解决跨域问题,cookie是可以带过去,但是节点B上面没有session啊,难不成又要登录一遍?解决办法就是利用Spring Session或者自己用Redis做session共享,或者直接在nginx上配置ip_hash,使得同一个ip始终路由到同一个节点。
虽然session仍然有缺陷,但是现在大部分的Web应用还在使用session认证。后来出现了token认证。
token认证
token又叫令牌,本质上就是一串无意义的字符串,一般放在请求头里,请求头key一般是"Authorization",当然也可以和服务端约定好自定义成其他的,只要服务端能够从请求头中拿到token就好了。token认证的出现最大的特点就是让登陆认证不再依赖于cookie机制了,将token放在了请求头里,那些由于cookie机制导致的弊端自然就没有了。
token认证的流程图
优缺点
- token认证最大的好处就是支持移动设备,因为移动端是不支持cookie的,自然cookie认证和session认证就用不了了。又不依赖cookie机制,
- 用户信息是服务端管理的,
- 每次请求都要用token去数据库中查询用户信息,数据库的压力太大了。
如果token携带了用户信息,不就不需要每次请求都访问数据库查了嘛,从token中直接解析出用户信息以及用户登录状态进行校验,这就是后来的JWT。给浏览器返回的token是一串带着用户信息的字符串
JWT认证
JWT全称是Java Web Token。其实就是特殊的token,理解起来就是携带着用户信息的token。所以JWT认证和token认证本质上是一样的。只不过token认证的用户信息是从数据库里查的。而JWT认证的用户信息是直接从token解析出来的。
JWT认证流程图
基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库。
基于session认证的单点登录单点登录
基于session认证的单点登录还有局部会话和全局会话两个概念,即浏览器和应用服务器之间建立的是局部会话。和认证中心服务器之间建立的全局会话。
也就是用户登录成功之后、在认证中心服务器和应用服务器都保存用户信息和登录状态的session,浏览器中的表现就是应用服务和认证中心服务的域下面都会存着对应的sessionID的cookie;
基于session认证的单点登录流程图
需要注意的是,基于session认证的单点登录在集群环境中是不需要做session共享或者nginx配置ip_hash的。集群中各个节点就相当于各个子系统了,没有局部会话session那就重新走一遍自动单点认证,反正不需要用户手动再输一遍密码就好了。
java实现基于session认证的单点登录需要两个jar包,cas-client-core和cas-server-core,即cas客户端和服务端,cas客户端就是图中的服务器A和B,cas服务端就是认证中心。这里主要看一下cas-client-core这个jar包,从中捋出部分源码对应一下图中的流程。
在AuthenticationFilter的doFilter()方法中,服务端校验如果局部会话和ticket都没有,则将浏览器重定向到单点登录地址:
java实现基于session认证的单点登录需要两个jar包,cas-client-core和cas-server-core,即cas客户端和服务端,cas客户端就是图中的服务器A和B,cas服务端就是认证中心。这里主要看一下cas-client-core这个jar包,从中捋出部分源码对应一下图中的流程。
在AuthenticationFilter的doFilter()方法中,服务端校验如果局部会话和ticket都没有,则将浏览器重定向到单点登录地址:
基于JWT的单点登录
其实单点登录的过程都差不多,就是认证方式的区别,理解了基于session认证的单点登录后,也很好理解基于JWT认证的单点登录。
Cookie 和 Session 的区别
-
安全性: Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
-
存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型
-
有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
-
存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。
Token 和 Session 的区别
-
Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。 而Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
-
Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
Token 和 JWT 的区别
相同
- 都是访问资源的令牌
- 都可以记录用户的信息
- 都是使服务端无状态化
- 都是只有验证成功后,客户端才能访问服务端上受保护的资源
区别
Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证Token 是否有效。
JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
使用 cookie 时需要考虑的问题
- 因为存储在客户端,容易被客户端篡改,使用前需要验证合法性不要存储敏感数据,比如用户密码,账户余额;
- 使用 httpOnly 在一定程度上提高安全性
- 尽量减少 cookie 的体积,能存储的数据量不能超过 4kb
- cookie 无法跨域
- 一个浏览器针对一个网站最多存 20 个Cookie,浏览器一般只允许存放 300 个Cookie
- 移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是token
使用 session 时需要考虑的问题
- 将 session 存储在服务器里面,当用户同时在线量比较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session
- 当网站采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。当多个应用要共享 session 时,
- 除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。
- sessionId 是存储在 cookie 中的,假如浏览器禁止 cookie 或不支持 cookie 怎么办? 一般会把sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现
- 移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是token
分布式架构下 session 共享方案
session 复制
任何一个服务器上的 session 发生改变(增删改),该节点会把这个 session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 session ,以此来保证 session同步
优点: 可容错,各个服务器间 session 能够实时响应。
缺点: 会对网络负荷造成一定压力,如果 session 量大的话可能会造成网络堵塞,拖慢服务器性能
粘性 session /IP 绑定策略
采用 Ngnix 中的 ip_hash 机制,将某个 ip的所有请求都定向到同一台服务器上,即将用户与服务器绑定。 用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 session 的话,那么用户以后的每次请求都会转发到 A 服务器上,相当于把用户和 A 服务器粘到了一块,这就是粘性 session 机制。
优点: 简单不需要对 session 做任何处理。
缺点: 缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 session信息都将失效。
适用场景: 发生故障对客户产生的影响较小;服务器发生故障是低概率事件。实现方式: 以 Nginx 为例,在 upstream 模块配置 ip_hash 属性即可实现粘性 session。
session 共享(常用)
使用分布式缓存方案比如 Memcached 、Redis 来缓存 session,但是要求 Memcached 或 Redis必须是集群。
把 session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问一次 Redis ,但是这种方案带来的好处也是很大的:
实现了 session 共享;
可以水平扩展(增加 Redis 服务器);
服务器重启 session 不丢失(不过也要注意 session 在 Redis 中的刷新/失效机制);
不仅可以跨服务器 session 共享,甚至可以跨平台(例如网页端和 APP 端)
session 持久化
将 session 存储到数据库中,保证 session 的持久化
优点: 服务器出现问题,session 不会丢失
缺点: 如果网站的访问量很大,把 session 存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。