🔰Session、Cookie、Token
type
status
date
slug
summary
tags
category
icon
password
原文
CookieCookie的重要属性Cookie 的应用案列Cookie的缺陷SessionSession 认证流程Session 的缺陷分布式 Session 方案Cookie 和 Session 的区别Token Token的作用Token的优点常见的 Token 类型Token的生成规则OAuth TokenAccess Token (访问令牌)Refresh Token (刷新令牌)双Token(无感刷新)为什么不能自动给 Refresh Token 续期?Refresh Token 失效的处理方法Session-Cookie 方案进行身份认证没有Cookie的情况下 Session 还能使用吗?多服务器节点下 Session-Cookie 方案如何做?如何解决?为什么 Cookie 无法防止 CSRF 攻击而 token 则可以?为什么 Token比 Session、Cookie 更适合移动端?为什么 Session 不适合移动端?
HTTP 协议是一种无状态的协议。每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,上一次请求的发送者和这一次的发送者是不是同一个人,所以
浏览器
为了进行会话跟踪,就必须进行维护一个状态,用于告知服务端两个请求是否来自同一浏览器,而这个状态需要通过 Session 或 Cookie 来实现。Cookie
Cookie 和Sesstion
都是用来跟踪浏览器用户身份的会话方式
,但是两者应用场景不太一样。这里注明了Session、Cookie是跟踪
浏览器
用户身份,在以跨应用的服务中,基本上都是以 Token 进行身份认证。Cookie是某些网站为了辨别用户身份存储在用户本地终端上的数据。
Cookie 是不可跨域的
:每个 Cookie 会绑定单一的域名,无法在别的域名下获取使用。一级域名和二级域名之间是允许共享使用的(靠的是 domain)。一级域名(顶级域名):这是域名的最高层级,通常由一个顶级域名(Top-level Domain,TLD)和一个域名注册商的名称组成。例如,.com、.net、.org等是常见的顶级域名。一级域名通常表示网站的性质或类型,如商业(.com)、网络服务(.net)或组织(.org)。二级域名:这是在一级域名之下的一个更具体的子域名。它可以用来进一步细分和标识网站的特定部分或业务。例如,在
Cookie的重要属性
属性 | 说明 |
name=value | 键值对,设置 Cookie 的名称及相对应的值,都必须是字符串类型
- 如果值为 Unicode 字符,需要为字符编码。
- 如果值为二进制数据,则需要使用 BASE64 编码。 |
domain | 指定 cookie 所属域名,默认是当前域名 |
path | 指定 cookie 在哪个路径(路由)下生效,默认是 '/'。
如果设置为 /abc,则只有 /abc 下的路由可以访问到该 cookie,如:/abc/read。 |
maxAge | cookie 失效的时间,单位秒。如果为整数,则该 cookie 在 maxAge 秒后失效。如果为负数,该 cookie 为临时 cookie ,关闭浏览器即失效,浏览器也不会以任何形式保存该 cookie 。如果为 0,表示删除该 cookie 。默认为 -1。-比 expires 好用。
|
expires | 过期时间,在设置的某个时间点后该 cookie 就会失效。
一般浏览器的 cookie 都是默认储存的,当关闭浏览器结束这个会话的时候,这个 cookie 也就会被删除 |
secure | 该 cookie 是否仅被使用安全协议传输。安全协议有 HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。
当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。 |
httpOnly | 如果给某个 cookie 设置了 httpOnly 属性,则无法通过 JS 脚本 读取到该 cookie 的信息,但还是能通过 Application 中手动修改 cookie,所以只是在一定程度上可以防止 XSS 攻击,不是绝对的安全 |
Cookie 的应用案列
- 我们在 Cookie 中保存已登录过的用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了。除此之外还能保存用户的首选项,主题或其他的设置信息。
- 使用 CooKie 保存 SessionId 或 Token,向后端发送请求的时候带上 Cookie,这样后端就能取到Session 或者 Token。这样就能记录到用户当前的状态了,因为 HTTP 协议是无状态的。
- Cookie 还可以记录和分析用户的行为,因为就 HTTP 协议是没有状态的,如果服务器想要获取你在某个页面的停留状态或者看了哪些商品,一种常用的实现方式就是将这些信息存在在 Cookie
Cookie的缺陷
存储限制
:存储的大小和类型有限制
安全性
:用户可以直接通过浏览器拿到 Cookie 中的信息(所以使用 Cookie 中存储信息需要脱敏)
Session
- Session 是一种记录服务器和客户端会话的机制。
- Session 是基于 Cookie 实现的,Session 存储在服务端,SessionId 是会被存储到客户端的 Cookie 中。
Session 认证流程
- 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session。
- 请求返回时将此 Session 的唯一标识信息 Session ID 返回给浏览器
- 浏览器接收到服务器返回的 Session ID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
- 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 Session ID,再根据 Session ID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
Session ID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此理来验证用登陆状态。
Session 的缺陷
- 依赖服务器存储,难以扩展
- 难以实现细粒度的权限控制:在分布式的系统中,当一个用户请求A服务器的之后,该服务器A 创建了一个 Session。当第二次请求服务器 B 时,该服务器上找不到对应的 Session 信息。
- 分布式架构下的复杂性:在多服务器或分布式环境中,Session 需要共享或同步,导致管理复杂。常见的解决方案如 Session 复制、集中式缓存(如 Redis)等,但会引入额外的维护和网络延迟。
- 会话管理开销大
占用服务器资源:每个用户会话都需要在服务器端维持一个 Session,会占用内存等资源;当用户数大时,服务器资源会被大量 Session 占用,影响应用性能。
- 不便于跨平台和跨域使用
- 跨域限制:Session 依赖 Cookie 存储会话 ID,而 Cookie 在跨域场景中收到限制,不适合跨域应用或不同子域名下的 SSO 场景。
- 移动端支持不佳:移动应用无法自动附带 Cookie,往往需要手动管理。
- 安全性问题
- 会话劫持风险:SessionId 存储在 Cookie中,容易受到会话劫持攻击,如XSS 或 CSRF 攻击。
- 依赖 Https保护:如果未使用 HTTPS,SessionId 会通过明文传输,增加被窃取的风险。(现代认证方案通过 Token 可以规避该问题)
分布式 Session 方案
- 客户端存储
- 安全性问题
- Cookie的数据类型及大小限制
Session 信息直接存在客户端(浏览器),保证 Session 一致性。
缺点:
- Session 复制同步
- 每个服务器都全量保存了 Session 信息,造成服务器资源浪费。
- 实现复杂(Tomcat等 web 容器都支持 Session 复制功能)
将服务器的 Session 同步到同步各个服务器中。
缺点:
既然是同步就有延迟、数据一致性问题等要去解决。
- 负载策略
- 利用 nginx 使用 ip_hash 负载策略,将客户端和服务器进行绑定。
- 登录时根据用户的标识符如用户 id,来进行用户和服务器的绑定。
缺点:
服务器宕机了,那该台服务器的 Session 信息就丢失了。在如今讲究高可用的前提下,该方案直接会被 PASS。
- Session 集中管理
将所有的服务器的 Session 进行统一管理,例如服务端将 Session 信息都存到 Redis 、MySQL、MongoDB中。
Spring-Session
框架就提供了对管理用户会话的 Api 和实现。Cookie 和 Session 的区别
Session 的主要作用是通过服务端记录用户的状态。有些场景系统是需要知道是哪个用户操作的,因为 HTTP 协议是无状态的,服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户
- 安全性 :cookie 数据保存在客户端(浏览器端),Session 数据是保存在服务端。相对来说,Session 安全性更高。
- 有效期不同:
Cookie 可设置长时间保持,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效
。
- 存储类型不同:Cookie只支持存储字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
Token
Token(令牌)是一种用于身份验证和授权的标识,通常用于服务端和客户端之间交互使用。Token 是一种简便的,用于验证用户身份、控制访问权限的一种机制。
Token的作用
- 身份验证:Token 可用于证明用户的身份,类似于登录后的“通行证”。
- 授权:Token 可以被用来控制用户对资源的访问权限,基于用户身份给予不同的权限。
- 信息传递:Token 也可以携带用户信息,在多个系统间传递必要的用户数据。
Token的优点
使用 Token 机制的优点
Token支持跨域访问,
将 token 置于请求头中,而Cookie是不支持跨域访问
。
- 无状态化,服务端无需存储 token,只需要验证 token 信息是否正确,而 session 需要在服务端存储,一般是通过 cookie 中的 sessionId 在服务端查找对应的 session。
- 无需绑定到一个特殊的身份验证方案(传统的用户名密码登陆),只需要生成的 token 符合我们预期的设定的即可。
- 更适用于移动端(Android、IOS、小程序等,像这种原生平台不支持 Cookie,比如说微信小程序,每一次请求都是一次会话,当然我们可以手动为他添加 Cookie。
- 避免 CSRF攻击。
常见的 Token 类型
- Session Token:与服务器端的会话机制关联,通常存储在服务器端,客户端通过 Cookie 或 SessionID访问。
- JWT(JSON Web Token):一种广泛运用的无状态 Token ,携带用户信息,客户端和服务器之间可以直接传递。
- OAuth Token: 用于授权标准, 应用在第三方的系统授权访问中,分为 Access Token 和 Refresh Token。
- API Token:访问 API的场景,服务器根据该 Token 确认请求方是否有权限。
Token的生成规则
在实际开发中,有的服务端会遵循JWT 的规范生成 Token,但不是必须如此,但无论怎样,一般都不会设计成一个明文字符串的。
JWT生成的 Token 是一段较长的字符串,这带来了校验、生成、传输、存储上的成本,这是考量是否要是使用 JWT 的最重要的方面。
- 利用 Session Id 来生成 Token。
- UUID
OAuth Token
OAuth Token是在 OAuth(开放授权)协议中的使用的一种授权凭证,允许应用程序在用户的许可下访问服务或资源而不需要直接暴露用户的凭据(如用户名和密码)。
OAuth Token 主要有两种类型:Access Token 和 Refresh Token
Access Token (访问令牌)
- 访问资源接口(api)时所需要的资源凭证
- 简单的 token 组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token的前几位以哈希算法压缩成的一定长度的十六进制字符串)
- 特点:
- 服务端无状态化、可扩展性好
- 支持移动端设备
- 安全
- 支持跨程序调用
- 流程
- 每一次请求都需要携带 token,需要把 token放到 HTTP 的 Header 里。
- token 完全由应用管理,所以它可以避开同源策略(支持跨域)。
Refresh Token (刷新令牌)
Refresh Token 是一种长期有效的 Token
,它用于 Access Token 过期后获取一个新的 Access Token,无需用户再次进行登录授权。它在第一次获得 Access Token 时一并生成。Refresh Token 是专用与刷新 Access token的 token。如果没有Refresh Token,也可以刷行刷新 Access Token,但每次刷新都需要用户输入用户名与密码,影响用户体验,有了 Refresh Token 避免了该麻烦。客户端直接 Refresh token 直接更新Access Token,无需额外操作。
双Token(无感刷新)
大部分开发人员所说的 双 Token,无感刷新,其实就是 OAuth Token 的方案。不要介意命名,关注点放在其设计。还有一点要声明的是,双 Token 都是需要客户端或前端来配合的。
第一个是 Access Token,它的过期时间就是 Access Token 本身的过期时间,例如半小时,另外一个是 Refresh Token 它的过期时间 比如一天。RefreshJWT只用来获取 AccessJWT,不容易泄露。
客户端登陆后,将 Access Token 和 Refresh Token都保存在本地,每次访问后端服务将 Access Token传给服务端,服务端校验 Access Token的有效性。
如果过期,就将 Refresh Token传给服务端。
如果有效,服务端就生成新的 Access Token给客户端。否则,客户端就重新登录。
这里的双 Token 方案,也是常说的无感刷新 Token,是需要客户端配合开发的。
就上面说的 双 Token 流程,需要注意的几点。
- 提前刷新策略,一般是客户端是会在 Access Token 失效的前几分钟去请求服务端,更新 Access Token。
- 如果 Refresh Token 也已经失效了,让客户端引导客户重新登录。
为什么不能自动给 Refresh Token 续期?
OAuth 2.0 设计的初衷是为了保障安全。Refresh Token 自动续期会带来安全隐患。
OAuth 2.0的规范中,Refresh Token 通常用于刷新 Access Token 的凭据,但 Refresh Token 自身是不能通过自动续期的方式进行更新或延长其有效期的。
- 安全风险
如果 Refresh Token 自动续期且客户端无需用户再次认证,它可能被攻击者滥用。攻击者一旦获取到 Refresh Token,便可以长时间持续 Access Token,进行无授权的访问。
- 用户控制
自动续期可能让用户对授权的控制。Refresh Token 的失效是为了确保用户在特定时间内对应用的访问授权。自动续期会让授权时间变得不透明,用户无法明确掌控。
如果一个应用你只需要登录一次,那其实也就不需要采用该双 Token方案了。双 Token (OAuth Token)是为了解决 JWT 协议中的关于 Token续期问题。如果过期时间短需要每隔几个小时登录一次造成用户体验不佳。
- 防止长期滥用
允许 Refresh Token 失效的目的是确保长期不活动的客户端失去访问权,这样如果一个设备或应用不再使用,它的凭据也不会永久有效。
在很多涉及到钱的APP,如各大银行的 App 中,客户端超过半个小时(甚至更短)不活动,应用就会自动退出。再次使用需要重新登录,这也可以一定程度上保证安全。怎么设计Token 的有效期取决于你的业务,给出一个”合理值”,当然一些 web 页面,可能给出一个用户一个选择按钮,例如 7 天、15、30 免登录。一般建议该有效期都不会超过30 天。受信任的网络安全环境或者私有应用除外。
Refresh Token 失效的处理方法
- 客户端引导用户重新登录
- 用户主动注销或撤销权限
当用户主动注销或再某些设备上撤销了授权,客户端应该检测到 Refresh Token 无效,并清除所有存储的凭据(服务端也是如此,一般注销会发送请求到后端,后端会处理用户注销的相关业务逻辑),确保用户的安全。
- 替代策略:缩短 Access Token 过期时间+滚动刷新机制。
有些授权服务器允许使用 Refresh Token 刷新 Access Token时,返回新的 Refresh Token,这种机制通常被称为“滚动刷新”。
Session-Cookie 方案进行身份认证
补充流程图
使用 Session 的时候需要注意以下 三点
- 依赖 Session 的关键业务一定要确保客户端开启了 Cookie。
- 注意 Session 的过期时间
没有Cookie的情况下 Session 还能使用吗?
一般是 通过 Cookie 来保存 SessionID,假如你使用了 Cookie 保存 SessionID的方案的话,如果客户端禁用了 Cookie,那么 Session 就无法正常工作。
在禁用 Cookie 的情况下,你可以把 SessionID放在请求 url 里面。
多服务器节点下 Session-Cookie 方案如何做?
Session-Cookie 在单体环境中是一个非常好的身份认证方案。当服务器水平扩展到多节点时,该方案就面临挑战。
例如:部署了两份相同的服务 A、B,用户第一次登陆的时候,Ngnix 通过负载均衡将用户的请求转发给 A 服务器,此时用户的 Session 信息保存在 A 服务器。结果用户第二次访问的时候 ,Ngnix 转发到 B 服务器,由于 B 服务器没有保存用户的 Session 信息,导致用户需要重新进行登陆。
如何解决?
- 通过 Hash策略分配给同一个服务器处理,这样的话每个服务器都保存了一部分的用户信息。
- Session 同步,所有服务器的 Session 信息进行互相同步,也就是每一个服务器保存了全量的 Session 信息。每当一个服务器的 Session 信息发生变化,需要将其同步到其他服务器。该方案的缺点为成本大,节点越多则同步成本越高。还有一个缺点为复杂度,同步的过程要考虑宕机、网络等因素。
- 单独使用一个所有服务器都能访问到的数据节点比如缓存来存放 Session 信息,为了保证高可用,数据节点尽量避免是单点。
- Spring Session 是一个用于多个服务器之间管理会话的项目。它可以与多种后端存储如 Redis、MongoDB等集成,从而实现分布式会话管理。通过 Spring Session,可以将会话数据存储在共享的外部存储中,以实现跨服务的会话共享和同步。
为什么 Cookie 无法防止 CSRF 攻击而 token 则可以?
简单来说就是利用你的身份去发送一些对你不友好的请求。
例如你在一个网上银行的社区的帖子区,看到一个帖子的链接写着点击领取红包,你点开链接结果发现自己账上少了 10000元。这是怎么办到的呢,原来发帖人在链接中隐藏了一个请求,该请求是直接用你的身份给银行的请求发送了一个转账的请求,也就是通过你的 Cookie 向银行发送请求。
<a src=http://www.mybank.com/Transfer?bankId=11&money=10000>领取红包</>
上面也提到过,进行 Session 认证的时候,我们一般使用 Cookie 进行存储 SessionId,当我们登录到后端生成一个 SessionId 放在 Cookie 中返回客户端,服务端通过 Redis 或其他存储方式来保存这个 SessionId,客户端每次请求都会带上这个 SessionId,服务端通过这个 SessionId 来标识你这个人。如果别人通过 Cookie 拿到了 SessionId 后就可以代替你的身份访问该系统了。
Session认证中的 Cookie 中的 SessionId 由浏览器发送到服务端的,借助这个特性,攻击者就可以通过让用户误点攻击链接达到攻击效果。
用户登录成功获取 Token 以后,一般会选择存放在 localStorage(浏览器本地存储)中。然后我们在前端通过某些方式会给每个发送到后端的请求加上这个 Token,这样就不存在 CSRF 漏洞的问题。
因为即使点击了非法链接发送了请求到服务端,这个非法请求是不会携带 Token
,所以这个请求是非法的。无论是 Cookie 还是 Token 都无法避免跨站脚本攻击(Cross Site Scripting)XSS。
XSS中攻击者会用各种方式将恶意代码注入到其他用户的页面中。就可以通过脚本盗用信息比如 Cookie。
为什么 Token比 Session、Cookie 更适合移动端?
- 跨平台支持
Token 可以在不同平台之间(如移动端、Web 端和 API 服务)方便地传递和使用,且无需依赖浏览器环境,非常适合移动端或其他非浏览器环境的应用。
- 无状态认证
Token(如 JWT)是自包含的,包含用户身份和过期时间等信息,服务器无需在内存或数据库中存储会话状态。这种无状态的方式非常适合分布式系统,因为任何服务器节点都能验证 Token 的有效性,不需要共享 Session 数据。简单的来说就是扩展性强
这里说的客户端说的是 IOS、Android、Web、桌面端。
为什么 Session 不适合移动端?
- 状态管理:Session 是基于服务端的状态管理,而移动端应用通常是无状态的,移动端的设备连接状态可能不稳定或终端,难以维护长期的会话状态。
如果是使用 Session 认证,移动应用需要频繁的于服务器进行会话维护,增加了网络开销和复杂性。
- 兼容性:移动端应用通常面向多个平台 如 IOS、Android和 Web。每个平台对于 Session 的管理和存储方式可能不同,可能导致跨平台的兼容性处理问题。
- 安全性:移动设备通常处于不受信任的网络环境,存在数据泄露和攻击的风险。将敏感的会话信息存储在移动设备上增加了被攻击的潜在风险。
- 扩展性: Session 扩展性相对于 Token 是更差的,如果是集群方式情况下,你的登录请求命中了哪个服务器,Session 是会在对应的服务器上存储。
Prev
Spring源码阅读-事务
Next
kafka 源码阅读-生产者
Loading...