Cookie、Session、Token、JWT
认证(Authentication)
验证当前用户的身份
互联网中的认证:
用户名密码登录
邮箱发送登录链接
手机号接收验证码
只要你能收到邮箱/验证码,就默认你是账号的主
授权(Authorization)
用户授予第三方应用访问该用户某些资源的权限
实现授权的方式有:cookie、session、token、OAuth
凭证(Credentials)
实现认证和授权的前提是需要一种媒介(证书) 来标记访问者的身份
当用户登录成功后,服务器会给该用户使用的浏览器颁发一个令牌(token),这个令牌用来表明你的身份,每次浏览器发送请求时会带上这个令牌
Cookie
HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息),请求独立,服务端无法确认当前访问者信息,为了进行会话跟踪,就必须主动的去维护一个状态,需要通过 cookie 或者 session 去实现。
cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
cookie 是不可跨域的: 每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的(靠的是 domain)。
cookie 重要的属性
Session
一种记录服务器和客户端会话状态的机制
基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中
session 认证流程:
用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session
请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器
浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作
Cookie 和 Session 的区别
安全性: Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的
存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型
有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效
存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源
Token(令牌)
Acesss Token
访问资源接口(API)时所需要的资源凭证
简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
特点:服务端无状态化、可扩展性好、支持移动端设备、安全支持跨程序调用
token 的身份验证流程
-
客户端使用用户名跟密码请求登录
-
服务端收到请求,去验证用户名与密码
-
验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
-
客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
-
客户端每次向服务端请求资源的时候需要带着服务端签发的 token
-
服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据
-
每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里
-
基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库
-
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 和 Session 的区别
session :一种会话状态机制
token: 令牌,访问API的资源凭证,安全性较高,需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态
Session 认证只是简单的把 User 信息存储到 Session 里,只要有此 SessionID ,即认为有此 User 的全部权利
Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App
用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。
JWT
JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。
是一种认证授权机制
一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息
HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名
生成 JWT
-
https://jwt.io/
-
https://www.jsonwebtoken.io/
JWT 的原理
JWT 认证流程:
用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT
客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)
当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样
Authorization: Bearer <token>
-
服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为
-
因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要
-
因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)
-
因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制
JWT 的使用方式
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。
方式一
当用户希望访问一个受保护的路由或者资源的时候,放在 HTTP 请求头信息的 Authorization 字段里,使用 Bearer 模式添加 JWT
- GET /calendar/v1/events
- Host: api.example.com
- Authorization: Bearer <token>
-
用户的状态不会存储在服务端的内存中,这是一种 无状态的认证机制
-
服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为。
-
由于 JWT 是自包含的,因此减少了需要查询数据库的需要
-
JWT 的这些特性使得我们可以完全依赖其无状态的特性提供数据 API 服务,甚至是创建一个下载流服务。
-
因为 JWT 并不使用 Cookie ,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)
方式二
跨域的时候,可以把 JWT 放在 POST 请求的数据体里。
方式三
-
通过 URL 传输
Token 和 JWT 的区别
相同:
-
都是访问资源的令牌
-
都可以记录用户的信息
-
都是使服务端无状态化
-
都是只有验证成功后,客户端才能访问服务端上受保护的资源
区别:
-
Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效
-
JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
常见的前后端鉴权方式
-
Session-Cookie
-
Token 验证(包括 JWT,SSO)
-
OAuth2.0(开放授权)
使用 cookie 时需要考虑的问题
储存客户端,用户可修改,使用前需校验,
不要保存敏感信息
无法跨域
减小体积
正确的 domain 和 path,减少数据传输
移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token
网站最多20个cookie,浏览器最多300个
使用 session 时需要考虑的问题
session在服务端,用户同时在线数量较多,占用内存,需要定期清理过期session
集群部署时,服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息
共享 session 时,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理
sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现
移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token
使用 token 时需要考虑的问题
数据库来存储 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 协议传输
使用加密算法时需要考虑的问题
绝不要以明文存储密码
永远使用 哈希算法 来处理密码
分布式架构下 session 共享方案
session 复制
一个服务器上的 session 发生改变(增删改),该节点会把这个 session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 session ,以此来保证 session 同步
优点: 可容错,各个服务器间 session 能够实时响应。
缺点: 会对网络负荷造成一定压力,如果 session 量大的话可能会造 成网络堵塞,拖慢服务器性能。
粘性 session /IP 绑定策略
采用 Ngnix 中的 ip_hash 机制,将某个 ip的所有请求都定向到同一台服务器上,即将用户与服务器绑定。
优点: 简单,不需要对 session 做任何处理。
缺点: 缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 session 信息都将失效。
session 共享(常用)
使用分布式缓存方案比如 Memcached 、Redis 来缓存 session,但是要求 Memcached 或 Redis 必须是集群
把 session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问一次 Redis ,但是这种方案带来的好处也是很大的:
-
实现了 session 共享;
-
可以水平扩展(增加 Redis 服务器);
-
服务器重启 session 不丢失(不过也要注意 session 在 Redis 中的刷新/失效机制);
-
不仅可以跨服务器 session 共享,甚至可以跨平台(例如网页端和 APP 端)
session 持久化
将 session 存储到数据库中,保证 session 的持久化
优点: 服务器出现问题,session 不会丢失
缺点: 如果网站的访问量很大,把 session 存储到数据库中,会对数 据库造成很大压力,还需要增加额外的开销维护数据库。
只要关闭浏览器 ,session 真的就消失了?
不对。是大部分 session 机制都使用会话 cookie 来保存 session id,而关闭浏览器后这个 session id 就消失了,再次连接服务器时也就无法找到原来的 session。
由于关闭浏览器不会导致 session 被删除,迫使服务器为 session 设置了一个失效时间,当距离客户端上一次使用 session 的时间超过这个失效时间时,服务器就认为客户端已经停止了活动,才会把 session 删除以节省存储空间。
是一种认证授权机制