请教一些关于cookie和session的问题(默认情况下,不设置失效时间)

china_codechina_code · 2022-04-27 17:14
请教一些关于cookie和session的问题(默认情况下,不设置失效时间) 1、cookie的生命周期 1.1、cookie是不是只有访问游览器才有,关闭游览器cookie就消失了 2、session的生命周期 2.1、session是不是只有页面发出请求的时候才会产生 2.2、session什么时候销毁 3、如果一个用户在一台电脑登录2个游览器,这个时候cookie和session是一个还是2个 4、cookie和session是不是只在PC端访问游览器的时候才会产生,手机app应用不会产生,或者手机和pc端产生的结果和方式不一样 5、只要有请求就会产生session吗,只要有游览器就会产生cookie吗
1 个回答
桥
能力有限

认证、授权与凭证


什么是认证(Authentication)?


  • 通俗地讲就是验证当前用户的身份是否合法的过程,即你是谁?证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统里录入的指纹相匹配时,就打卡成功)
  • 互联网中的认证:用户名密码登录;邮箱发送登录链接;手机号接收验证码。只要你能收到邮箱/验证码,就默认你是账号的主人!认证主要是为了保护系统的隐私数据与资源。
  • 拓展:什么是会话?认证通过后,为了避免用户每次操作都进行认证(除银行转账等),可以将用户信息保存在会话中,会话就是系统为了保存当前用户的登录状态所提供的机制,常见的有基于Session和token的方式,具体见下文。


什么是授权(Authorization)?


  • 简单来讲就是谁(who)对什么(what)进行了什么操作(how)。认证是保证用户的合法性,授权则是为了更细粒度的对隐私数据的划分。*授权是在认证通过后,控制不同的用户访问不同的资源。
  • 用户授予第三方应用访问该用户某些资源的权限。比如,你在安装手机应用的时候,APP 会询问是否允许授予权限(访问相册、地理位置等权限);你在访问微信小程序时,当登录时,小程序会询问是否允许授予权限(获取昵称、头像、地区、性别等个人信息)
  • 实现授权的方式有:业界通常基于R(role/resource)BAC实现授权:(1)基于角色的访问控制(2)基于资源(权限)的访问控制,系统设计时定义好某项操作的权限标识,系统扩展性好。


什么是凭证(Credentials)


  • 实现认证和授权的前提是需要一种媒介(证书) 来标记访问者的身份。
  • 例如:在战国时期,商鞅变法,发明了照身帖。照身帖由官府发放,是一块打磨光滑细密的竹板,上面刻有持有人的头像和籍贯信息。国人必须持有,如若没有就被认为是黑户,或者间谍之类的。在现实生活中,每个人都会有一张专属的居民身份证,是用于证明持有人身份的一种法定证件。通过身份证,我们可以办理手机卡/银行卡/个人贷款/交通出行等等,这就是认证的凭证。
  • 在互联网应用中,一般网站会有两种模式,游客模式和登录模式。游客模式下,可以正常浏览网站上面的文章,一旦想要点赞/收藏/分享文章,就需要登录或者注册账号。当用户登录成功后,服务器会给该用户使用的浏览器颁发一个令牌(token),这个令牌用来表明你的身份,每次浏览器发送请求时会带上这个令牌,就可以使用游客模式下无法使用的功能。


Cookie与Session



什么是Cookie?


  • HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。
  • cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
  • cookie 是不可跨域的: 每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的靠的是 domain)


cookie 重要的属性



什么是 Session


  • session 是另一种记录服务器和客户端会话状态的机制,即告诉服务端前后两个请求是否来自同一个客户端(浏览器),知道谁在访问我。因为http本身是无状态协议,这样,无法确定你的本次请求和上次请求是不是你发送的。如果要进行类似论坛登陆相关的操作,就实现不了了。
  • session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中。ps:还有一种是浏览器禁用了cookie或不支持cookie,这种可以通过URL重写的方式发到服务器;


session 认证流程(如上图):


  • 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session
  • 请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器
  • 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
  • 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。


根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。


基于Session的认证机制由Servlet规范定制,Servlet容器已经实现,用户通过HttpSession的操作方法可以实现:




Cookie 和 Session 的区别


  • 安全性:Session 是存储在服务器端的,Cookie 是存储在客户端的。所以 Session 相比 Cookie 安全,
  • 存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
  • 有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
  • 存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。



拓展:Session痛点


看起来通过 cookie + session 的方式是解决了问题, 但是我们忽略了一个问题,上述情况能正常工作是因为我们假设 server 是单机工作的,但实际在生产上,为了保障高可用,一般服务器至少需要两台机器,通过负载均衡的方式来决定到底请求该打到哪台机器上。



假设登录请求打到了 A 机器,A 机器生成了 session 并在 cookie 里添加 sessionId 返回给了浏览器,那么问题来了:下次添加购物车时如果请求打到了 B 或者 C,由于 session 是在 A 机器生成的,此时的 B,C 是找不到 session 的,那么就会发生无法添加购物车的错误,就得重新登录了,此时请问该怎么办。主要有以下三种方式:


(1 )session 复制


A 生成 session 后复制到 B, C,这样每台机器都有一份 session,无论添加购物车的请求打到哪台机器,由于 session 都能找到,故不会有问题



这种方式虽然可行,但缺点也很明显:


  • 同一样的一份 session 保存了多份,数据冗余
  • 如果节点少还好,但如果节点多的话,特别是像阿里,微信这种由于 DAU 上亿,可能需要部署成千上万台机器,这样节点增多复制造成的性能消耗也会很大。


(2)session 粘连


这种方式是让每个客户端请求只打到固定的一台机器上,比如浏览器登录请求打到 A 机器后,后续所有的添加购物车请求也都打到 A 机器上,Nginx 的 sticky 模块可以支持这种方式,支持按 ip 或 cookie 粘连等等,如按 ip 粘连方式如下


这样的话每个 client 请求到达 Nginx 后,只要它的 ip 不变,根据 ip hash 算出来的值会打到固定的机器上,也就不存在 session 找不到的问题了,当然不难看出这种方式缺点也是很明显,对应的机器挂了怎么办?


(3)session 共享


这种方式也是目前各大公司普遍采用的方案,将 session 保存在 redis,memcached 等中间件中,请求到来时,各个机器去这些中间件取一下 session 即可。



缺点其实也不难发现,就是每个请求都要去 redis 取一下 session,多了一次内部连接,消耗了一点性能,另外为了保证 redis 的高可用,必须做集群,当然了对于大公司来说, redis 集群基本都会部署,所以这方案可以说是大公司的首选了。


Token(令牌)与 JWT(跨域认证)



Token概述(no session!)


通过上文分析我们知道通过在服务端共享 session 的方式可以完成用户的身份定位,但是不难发现也有一个小小的瑕疵:搞个校验机制我还得搭个 redis 集群?大厂确实 redis 用得比较普遍,但对于小厂来说可能它的业务量还未达到用 redis 的程度,所以有没有其他不用 server 存储 session 的用户身份校验机制呢,使用token!


简单来说:首先请求方输入自己的用户名,密码,然后 server 据此生成 token,客户端拿到 token 后会保存到本地(token存储在浏览器端),之后向 server 请求时在请求头带上此 token 即可(server有校验机制,检验token合法性,同时server通过token中携带的uid确定是谁在访问它)。



可以看到 token 主要由三部分组成:


  • header:指定了签名算法
  • payload:可以指定用户 id,过期时间等非敏感数据
  • Signature: 签名,server 根据 header 知道它该用哪种签名算法,再用密钥根据此签名算法对 head + payload 生成签名,这样一个 token 就生成了。


当 server 收到浏览器传过来的 token 时,它会首先取出 token 中的 header + payload,根据密钥生成签名,然后再与 token 中的签名比对,如果成功则说明签名是合法的,即 token 是合法的。而且你会发现 payload 中存有我们的 userId,所以拿到 token 后直接在 payload 中就可获取 userid,避免了像 session 那样要从 redis 去取的开销。


你会发现这种方式确实很妙,只要 server 保证密钥不泄露,那么生成的 token 就是安全的,因为如果伪造 token 的话在签名验证环节是无法通过的,就此即可判定 token 非法。


可以看到通过这种方式有效地避免了 token 必须保存在 server 的弊端,实现了分布式存储,不过需要注意的是,token 一旦由 server 生成,它就是有效的,直到过期,无法让 token 失效,除非在 server 为 token 设立一个黑名单,在校验 token 前先过一遍此黑名单,如果在黑名单里则此 token 失效,但一旦这样做的话,那就意味着黑名单就必须保存在 server,这又回到了 session 的模式,那直接用 session 不香吗。所以一般的做法是当客户端登出要让 token 失效时,直接在本地移除 token 即可,下次登录重新生成 token 就好。


另外需要注意的是 token 一般是放在 header 的 Authorization 自定义头里,不是放在 Cookie 里的,这主要是为了解决跨域不能共享 Cookie 的问题


总结:token解决什么问题(为什么要用token)?


  • 完全由应用管理,可以避开同源策略
  • 支持跨域访问,cookie不支持, Cookie 跨站是不能共享的,这样的话如果你要实现多应用(多系统)的单点登录(SSO),使用 Cookie 来做需要的话就很困难了。但如果用 token 来实现 SSO 会非常简单,只要在 header 中的 authorize 字段(或其他自定义)加上 token 即可完成所有跨域站点的认证。
  • token是无状态的,可以在多个服务器间共享
  • token可以避免CSRF攻击(跨站请求攻击)
  • 易于扩展,在移动端原生请求是没有 cookie 之说的,而 sessionid 依赖于 cookie,sessionid 就不能用 cookie 来传了,如果用 token 的话,由于它是随着 header 的 authoriize 传过来的,也就不存在此问题,换句话说token 天生支持移动平台,可扩展性好


拓展1:那啥是CSRF呢?


攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过(cookie 里带来 sessionId 等身份认证的信息),所以被访问的网站会认为是真正的用户操作而去运行。


那么如果正常的用户误点了上面这张图片,由于相同域名的请求会自动带上 cookie,而 cookie 里带有正常登录用户的 sessionid,类似上面这样的转账操作在 server 就会成功,会造成极大的安全风险



CSRF 攻击的根本原因在于对于同样域名的每个请求来说,它的 cookie 都会被自动带上,这个是浏览器的机制决定的!


至于完成一次CSRF攻击必要的两个步骤:


1、首先登了一个正常的网站A,并且在本地生成了cookie

2、在cookie有效时间内,访问了危险网站B(就获取了身份信息)

Q:那我不访问危险网站就完了呗?

A:危险网站也许只是个存在漏洞的可信任网站!

Q:那我访问完正常网站,关了浏览器就好了呀?

A:即使关闭浏览器,cookie也不保证一定立即失效,而且关闭浏览器并不能结束会话,session的生命周期跟这些都没关系。


拓展2:同源策略?


  • 就是不同源的客户端脚本在没有明确授权情况下,不准读写对方的资源!
  • 同源就是:协议、域名与端口号都相同。
  • 同源策略是由 Netscape 提出的著名安全策略,是浏览器最核心、基本的安全功能,它限制了一个源中加载脚本与来自其他源中资源的交互方式。


拓展3:什么是跨域,如何解决?


  • 当浏览器执行脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域。
  • 产生原因:它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。
  • 解决方案
  • nginx(静态服务器)反向代理解决跨域(前端常用),a明确访问c代理服务器,但是不知道c的内容从哪里来,c反向从别的地方拿来数据。(忽略的是目标地址),浏览器可以访问a,而服务器之间不存在跨域问题,浏览器先访问a的服务器c,让c服务器作为代理去访问b服务器,拿到之后再返回数据给a。
  • jsonp:通常为了减轻web服务器的负载,我们把js、css、图片等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许。
  • 添加响应头


拓展4:易于扩展?


  • 比如有多台服务器,使用负载均衡,第一次登录转发到了A,A中seesion缓存了用户的登录信息,第二次登录转发到了B,这时候就丢失了登录状态,当然这样也是有解决方案可以共享session,但token只需要所有的服务器使用相同的解密手段即可。


拓展5:无状态?


  • 服务端不保存客户端请求者的任何信息,客户端每次请求必须自备描述信息,通过这些信息来识别客户端身份。服务端只需要确认该token是否是自己亲自签发即可,签发和验证都在服务端进行。


拓展6:什么是单点登录?


  • 所谓单点登录,是指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。



Acesss Token


  • 访问资源接口(API)时所需要的资源凭证
  • 简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)


特点:


  • 服务端无状态化、可扩展性好
  • 支持移动端设备
  • 安全性高
  • 支持跨程序调用



token 的身份验证流程:


  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
  4. 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据


注意点


  • 每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里
  • 基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库
  • token 完全由应用管理,所以它可以避开同源策略


Refresh Token


  • 另外一种 token——refresh token
  • refresh token 是专用于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。


两者区别

  • Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了。
  • Refresh Token 及过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求。


Token 的缺点


那有人就问了,既然 token 这么好,那为什么各个大公司几乎都采用共享 session 的方式呢,可能很多人是第一次听到 token,token 不香吗? token 有以下两点劣势:


  • token 太长了:token 是 header, payload 编码后的样式,所以一般要比 sessionId 长很多,很有可能超出 cookie 的大小限制(cookie 一般有大小限制的,如 4kb),如果你在 token 中存储的信息越长,那么 token 本身也会越长,这样的话由于你每次请求都会带上 token,对请求来是个不小的负担
  • 不太安全:网上很多文章说 token 更安全,其实不然,细心的你可能发现了,我们说 token 是存在浏览器的,再细问,存在浏览器的哪里?既然它太长放在 cookie 里可能导致 cookie 超限,那就只好放在 local storage 里,这样会造成安全隐患,因为 local storage 这类的本地存储是可以被 JS 直接读取的,另外由上文也提到,token 一旦生成无法让其失效,必须等到其过期才行,这样的话如果服务端检测到了一个安全威胁,也无法使相关的 token 失效。


所以 token 更适合一次性的命令认证,设置一个比较短的有效期!!!


拓展:不管是 cookie 还是 token,从存储角度来看其实都不安全(实际上防护 CSRF 攻击的正确方式是用 CSRF token),都有暴露的风险,我们所说的安全更多的是强调传输中的安全,可以用 HTTPS 协议来传输, 这样的话请求头都能被加密,也就保证了传输中的安全。


其实我们把 cookie 和 token 比较本身就不合理,一个是存储方式,一个是验证方式,正确的比较应该是 session vs token。


Token 和 Session 的区别


token和session其实都是为了身份验证,session一般翻译为会话,而token更多的时候是翻译为令牌;session和token都是有过期时间一说,都需要去管理过期时间;


  • Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息(可能保存在缓存、文件或数据库)。而 Token 是令牌访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
  • 其实token与session的问题是一种时间与空间的博弈问题,session是空间换时间,而token是时间换空间。两者的选择要看具体情况而定。虽然确实都是“客户端记录,每次访问携带”,但 token 很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证,每次当场得出合法 /非法的结论。这一切判断依据,除了固化在 CS 两端的一些逻辑之外,整个信息是自包含的。这才是真正的无状态。 而 sessionid ,一般都是一段随机字符串,需要到后端去检索 id 的有效性。万一服务器重启导致内存里的 session 没了呢?万一 redis 服务器挂了呢?
  • 所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。所以简单来说:如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。


JWT概述


  • JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。是一种认证授权机制
  • JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。
  • 可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的。
  • 阮一峰老师的 JSON Web Token 入门教程 讲的非常通俗易懂,这里就不再班门弄斧了


生成 JWT



JWT 的原理



JWT 认证流程:


  • 用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT
  • 客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)
  • 当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样
  • tion 中的 JWT 信息,如果合法,则允许用户的行为


更多内容: https://developer.aliyun.com/article/928965

赞同
反对
评论
收藏
2023-02-23 19:36
前往发表回答