会话技术及用户认证-Cookie、Session与 Token

前言

最早互联网只是用于简单的浏览文档信息、查看黄页及门户网站等等,并没有交互这个说法。但是随着互联网慢慢发展,宽带、服务器等硬件设施得到了很大的提示,互联网允许人们可以做更多的事情,所以交互式Web逐渐兴起,而HTTP无状态的特点却严重阻碍了其发展。
HTTP 是无状态的协议,每个请求都是完全独立的(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息)。也就是说,无法根据之前的状态进行本次的请求处理——服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。
因此,服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。

Cookie

在1994年,网景公司将Cookie的概念应用于网络通信,用来解决用户网上购物的购物车历史记录问题。而当时最强大的浏览器正是网景浏览器,在其支持下,其它浏览器也渐渐开始支持Cookie,到目前当然所有的浏览器都支持Cookie了。

既然Web服务器记不住东西,那么我们就在外部想办法记住,相当于服务器给每个客户端都贴上了一个小纸条。上面记录了服务器给我们返回的一些信息。然后服务器看到这张小纸条就知道我们是谁了,这个小纸条也就是Cookie。

Cookie是由服务器产生的而保存在客户端的一些数据,一般通过HTTP响应头Set-Cookie来设置,当然也可以通过JS脚本来直接设置,Cookie是按照网站来进行组织和保存的,每一个网站都可以在浏览器中保存一些Cookie,保存好了之后,浏览器向这个网站发出的请求都会携带这些Cookie,用于服务器记录客户端的状态。
例如,当我们登录网站勾选保存用户名和密码的时候,一般保存的都是cookie,将用户名和密码的cookie保存下来,这样再次登录的时候浏览器直接将cookie发送到服务端验证,直接username和password保存到客户端;再比如用户喜欢的网页背景色,这些信息也是可以通过Cookie保存到客户端的,这样登录之后直接浏览器直接就可以拿到相应的偏好设置。
Cookie主要用于以下三个方面:

  • 会话状态管理:如用户登陆状态、购物车商品或其它需要记录的信息
  • 个性化设置:如用户自定义设置、主题等
  • 浏览器行为跟踪:如跟踪分析用户行为等

构成

cookie遵循name=value格式的字符串形式,并且单个cookie保存的数据不能超过4K(很多浏览器都限制一个站点最多保存20个cookie)。主要构成有:

name: 一个唯一确定的cookie名称,服务器就是通过name属性来获取某个cookie值。通常来讲cookie的名称是不区分大小写的。

value: 存储在cookie中的字符串值,大多数情况下服务器会把这个value当作一个key取缓存中查询保存的数据。最好为cookie的name和value进行url编码

domain: cookie对于哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。顶级域名(如 .baidu.com)只能设置或访问顶级域名的Cookie,二级及二级以下域名( tieba.baidu.com)可以访问或设置自身或者顶级域名的Cookie(也就是说,如果要在多个二级域名中共享Cookie,只能将domain属性设置为顶级域名)。

path:  表示这个cookie影响到的路径,浏览器跟会根据这项配置,像指定域中匹配的路径发送cookie(只有该路径下的页面可以读取此cookie)。

expires: 失效时间,表示cookie何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个cookie)。如果不设置这个时间戳,浏览器会在页面关闭时即将删除所有cookie;不过也可以自己设置删除时间。这个值是GMT时间格式,如果客户端和服务器端时间不一致,使用expires就会存在偏差。

max-age:  与expires作用相同,用来告诉浏览器此cookie多久过期(单位是秒),而不是一个固定的时间点。正常情况下,max-age的优先级高于expires。

HttpOnly:  告知浏览器不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见。但在http请求张仍然会携带这个cookie。注意这个值虽然在脚本中不可获取,但仍然在浏览器安装目录中以文件形式存在。这项设置通常在服务器端设置。

secure:  安全标志,指定后,只有在使用SSL链接时候才能发送到服务器,如果是http链接则不会传递该信息。就算设置了secure 属性也并不代表他人不能看到你机器本地保存的 cookie 信息,所以不要把重要信息放cookie就对了。

会话cookie和持久cookie

cookie有过期时间设定。
如果不设置过期时间,则表示这个cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。
如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie依然有效直到超过设定的过期时间。
存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存的cookie,不同的浏览器有不同的处理方式。

Session

Cookie是存在客户端,且其本身存储的尺寸大小有限,最关键的问题是Cookie是用户可见的,并可以随意地修改,安全性存在很大问题。为了安全又方便地全局读取信息,一种新的存储会话机制——Session诞生了。
session是服务端存储的一个对象,主要用来存储所有访问过该服务端的客户端的用户信息(也可以存储其他信息),从而实现保持用户会话状态。但是服务器重启时,内存会被销毁,存储的用户信息也就消失了。
不同的用户访问服务端的时候会在session对象中存储键值(key-vaule)对,key是用来存储开启这个用户信息的“钥匙”,在登录成功后,“钥匙”通过cookie返回给客户端,客户端存储为session Id记录在cookie中。当客户端再次访问时,会默认携带cookie中的session Id来实现会话机制。
简单来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时由于在服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。

 

 

Session认证流程

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

若浏览器禁止cookie,可以通过URL重写的方式发送:把session id附加在URL路径的后面,附加的方式也有两种,一种是作为URL路径的附加信息,另一种是作为查询字符串附加在URL后面。网络在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。

分布式架构下 session 共享方案

Session的数据一般是存储到内存中,那问题就来了,如果我们的服务是分布式部署,有多台机器的话,可能我们第一次登陆的时候,我们把用户的信息存储到了session,但是后面的请求到了B机器上,那B机器是获取不到用户的session的。另外就是Session存储在内存中,那服务器重启,Session就丢失了,这就是它的弊端。现在有一些技术可以解决上述问题。
1. session 复制
任何一个服务器上的 session 发生改变(增删改),该节点会把这个 session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 session ,以此来保证 session 同步。

  • 优点: 可容错,各个服务器间 session 能够实时响应。
  • 缺点: 会对网络负荷造成一定压力,如果 session 量大的话可能会造成网络堵塞,拖慢服务器性能。

2. 粘性 session /IP 绑定策略
采用 Ngnix 中的 ip_hash 机制,将某个 ip的所有请求都定向到同一台服务器上,即将用户与服务器绑定。 用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 session 的话,那么用户以后的每次请求都会转发到 A 服务器上,相当于把用户和 A 服务器粘到了一块,这就是粘性 session 机制。

  • 优点: 简单,不需要对 session 做任何处理。
  • 缺点: 缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 session 信息都将失效。
  • 适用场景: 发生故障对客户产生的影响较小;服务器发生故障是低概率事件 。
  • 实现方式: 以 Nginx 为例,在 upstream 模块配置 ip_hash 属性即可实现粘性 session。

3. session 共享(常用)
使用分布式缓存方案比如 Memcached 、Redis 来缓存 session,但是要求 Memcached 或 Redis 必须是集群。把 session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问一次 Redis ,但是这种方案带来的好处也是很大的:

  • 实现了 session 共享;
  • 可以水平扩展(增加 Redis 服务器);
  • 服务器重启 session 不丢失(不过也要注意 session 在 Redis 中的刷新/失效机制);
  • 不仅可以跨服务器 session 共享,甚至可以跨平台(例如网页端和 APP 端)

4. session 持久化
将 session 存储到数据库中,保证 session 的持久化

  • 优点: 服务器出现问题,session 不会丢失
  • 缺点: 如果网站的访问量很大,把 session 存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。

Cookie和Session的区别

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上,当访问增多,会比较占用服务器的性能。考虑到减轻服务器负担方面,应当使用cookie。
4、存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。

因此,通常的做法是,将登陆信息等重要信息存放为session,而其他信息如果需要保留,可以放在cookie中。

Token

session 是保存了会话的链接信息与需要传输的内容,容量大,还需要保持会话信息,每次同一用户不同客户端访问都需要重新建立session,造成冗余信息大。
Token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。当用户第一次登录后,服务器生成一个token并将此token返回给客户端,以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。

 

 

token 的身份验证流程

1、客户端使用用户名跟密码请求登录
2、服务端收到请求,去验证用户名与密码
3、验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
4、客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 LocalStorage 里
5、客户端每次向服务端请求资源的时候需要带着服务端签发的 token
6、服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据
在这个过程中,每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里。
基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库。token 完全由应用管理,所以它可以避开同源策略。

Token 和 Session 的区别

1、Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
2、Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
3、所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。

Token的特点

Token的特点
• 随机性:每次的token都是不一样的
• 不可预测性:没有规律,无法预测
• 时效性: 可以设置token的有效时间
• 无状态、可扩展:由于只是一个算法,扩展起来非常方便
生成Token的解决方案有许多,常用的一种就是 JSON Web Token(JWT) .

JWT

JWT是目前最流行的跨域认证解决方案,可以使用在RESTFUL接口定义,也可以使用在普通的web。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。
JWT标准的Token由三部分组成:Header头部,Payload负载和Signature签名,三部分之间用“.”号做分割。
1、Header 声明信息。 在Header中通常包含了两部分:type:代表token的类型,这里使用的是JWT类型。 alg:使用的Hash算法,例如HMAC SHA256或RSA。{ “alg”: “HS256”, “typ”: “JWT” } 这会被经过Base64 URL编码形成第一部分

2、Payload token的第二个部分是荷载信息,它包含一些声明Claim(实体的描述,通常是一个User信息,还包括一些其他的元数据) 声明分三类: 
1)Reserved Claims:这是一套预定义的声明,并不是必须的,这是一套易于使用、操作性强的声明。包括:iss(issuer)、exp(expiration time)、sub(subject)、aud(audience)等 
2)Plubic Claims
3)Private Claims:交换信息的双方自定义的声明 { “sub”: “1234567890”, “name”: “John Doe”, “admin”: true } 
默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息(当然加密也没问题)加密的话推荐使用RSA加密。JSON对象也使用Base64 URL算法转换为字符串保存。

3、Signature 使用header中指定的算法将编码后的header、编码后的payload、一个secret进行加密。 例如使用的是HMAC SHA256算法,大致流程类似于: HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) 。这个signature字段被用来确认JWT信息的发送者是谁,并保证信息没有被修改 。

验证流程:

1. 在头部信息中声明加密算法和常量, 然后把header使用json转化为字符串
2. 在载荷中声明用户信息,同时还有一些其他的内容;再次使用json 把载荷部分进行转化,转化为字符串
3. 使用在header中声明的加密算法和每个项目随机生成的secret来进行加密, 把第一步分字符串和第二部分的字符串进行加密, 生成新的字符串,此字符串是独一无二的。
4. 解密的时候,只要客户端带着JWT来发起请求,服务端就直接使用secret进行解密;解签证解出第一部分和第二部分,然后比对第二部分的信息和客户端穿过来的信息是否一致。如果一致验证成功,否则验证失败。

常见问题总结补充

使用 cookie 时需要考虑的问题

  • 因为存储在客户端,容易被客户端篡改,使用前需要验证合法性
  • 不要存储敏感数据,比如用户密码,账户余额
  • 使用 httpOnly 在一定程度上提高安全性
  • 尽量减少 cookie 的体积,能存储的数据量不能超过 4kb
  • 设置正确的 domain 和 path,减少数据传输
  • 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

使用 token 时需要考虑的问题

  • 如果你认为用数据库来存储 token 会导致查询时间太长,可以选择放在内存当中。比如 redis 很适合你对 token 查询的需求。
  • token 完全由应用管理,所以它可以避开同源策略
  • token 可以避免 CSRF 攻击(因为不需要 cookie 了)
  • 移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token

JWT相关

  • 因为 JWT 并不依赖 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)
  • JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
  • JWT 不加密的情况下,不能将秘密数据写入 JWT。
  • JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
  • JWT 最大的优势是服务器不再需要存储 Session,使得服务器认证鉴权业务可以方便扩展。但这也是 JWT 最大的缺点:由于服务器不需要存储 Session 状态,因此使用过程中无法废弃某个 Token 或者更改 Token 的权限。也就是说一旦 JWT 签发了,到期之前就会始终有效,除非服务器部署额外的逻辑。
  • JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
  • JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态。
  • 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值