认证:Cookie、Session、Token、JWT(JSON Web Token)

  • 弥补HTTP没有状态的不足(无法判断两次HTTP请求是否来源于同一个用户)
  • 存储在客户端中
  • 不可跨域(依靠域名区分)
1
2
3
4
5
6
7
8
9
10
11
12
13
// 获取全部Cookie
function getAllCookies() {
let cookies = {};
const all = document.cookie;
if(all) {
const list = all.split("; ");
list.forEach(cookie=>{
const cookieArr = cookie.split("=");
cookies[cookieArr[0]] = decodeURIComponent(cookieArr[1]);
});
}
return cookies;
}

2. Session

  • 弥补HTTP没有状态的不足(无法判断两次HTTP请求是否来源于同一个用户)
  • 存储在服务端中
  • 基于Cookie实现(SessionId)

请求流程

  1. 用户第一次请求服务器的时候,服务端会根据用户输入的信息生成对应的Session
  2. 服务端在本次请求返回Session对应的SessionId给客户端(Set-Cookie)
  3. 浏览器会将SessionId存入Cookie中,同时Cookie会记录SessionId对应的Domain
  4. 用户第二次访问服务器的时候,请求将自动将Cookie中的SessionId发送到服务端上,服务端根据该SessionId查找对应的用户信息,若找不到,说明Session不存在或者已过期,要求用户重新输入信息。

Session的一个使用场景

  • Nginx负载均衡的时候:
    • 登录在服务器1,请求个人信息在服务器2,那么登录后请求个人信息又需要重新登录。
    • 用户将商品a加入购物车,而这个请求发送到服务器1,又将商品b加入购物车,这个请求发送到服务器2,最后订单结算,请求发送到服务器1,那么付款的时候,能看到的就只剩下商品a。

使用Session的时候需要考虑的问题

  • Session存储在服务器中的时候,如果用户使用量较大,需要定期清理过期的Session
  • 网站使用集群部署的时候,会遇到多台服务器共享Session的问题(即上面所述)
  • 多个服务器共享Session,还需要考虑Cookie跨域的问题
  • SessionId一般存储在Cookie中,但是如果浏览器禁止Cookie或不支持Cookie,一般把SessionId跟在URL参数后面重写URL(Session不一定需要Cookie
  • 移动端对Cookie的支持不是很好,Session一般依赖于Cookie,所以一般移动端应用Token

关闭浏览器后Session就永远消失了吗

不正确的。是因为大部分Session依赖于Cookie实现SessionId的保存,而关闭浏览器这个Cookie可能消失,这样再次链接服务器就会找不到原来的Session。如果服务器设置的Cookie通过硬盘或者其他手段改写HTTP请求头,把原来的SessionId发送给服务器,仍然能打开原来的Session

​ 因为关闭浏览器并不会导致Session被删除,所以服务器才会给Session设置一个过期的时间,当Session到期后,服务器才会删除Session节省空间

Session与Cookie

  • 安全性:Session存在服务端更加安全,Cookie设置HttpOnly可以一定程度避免XSS攻击
  • 存取值类型:Cookie只支持字符串类型,Session可以为任意类型
  • 有效期:Cookie可以设置为长时间保存,Session一般在客户端关闭或者设定的较短期限到期
  • 存储大小与个数限制:Cookie一般单个限制为4KB,限定个数与浏览器有关;而Session一般不存在限制,但是如果访问较多的话会给服务端带来性能影响

3. Token

一种简单的生成Token:

​ uuid(用户唯一标识)+时间戳+sign(hash处理token前几位生成的十六进制字符串)

特点

  • 服务端无状态化,可扩展性良好
  • 支持移动端设备
  • 安全
  • token完全由应用管理,可以避开同源策略

3.1 Access Token

  1. 客户端发送用户名密码等验证信息
  2. 服务端收到请求后进行验证,验证通过后,根据登录凭证加密为Access Token后与数据一起返回给客户端
  3. 客户端收到Token后,一般存储到localStorage中
  4. 客户端再次发起请求时,把Token放在请求头中
  5. 服务端收到请求后,查询数据库信息验证Token,验证成功后返回数据

3.2 Refresh Token

AccessToken存在过期时间(服务端存在数据库内),当Access Token过期后,用户需要重新输入验证信息。为了方便用户,还有一个Refresh Token。

  1. 客户端发送用户名密码等验证信息
  2. 服务端收到请求后进行验证,验证通过后,根据登录凭证加密为AccessToken与RefreshToken(RefreshToken过期时限>AccessToken)后与数据一起返回给客户端
  3. 客户端收到Token后存放在本地(一般为localStorage)
  4. 当客户端发送请求的时候将Access Token传输到服务端,服务端进行校验与过期检查,如果没有过期则返回数据
  5. 如果AccessToken过期,服务端向客户端发送过期错误,此时客户端将RefreshToken发送到服务端进行检验
  6. 如果RefreshToken未过期,则发送新的Access Token与 Refresh Token给客户端;否则,客户端需要重新输入登录验证信息

Token与Session

  • Session用于保持会话,使服务端状态化;而Token是访问资源(API)的一个凭证令牌,会使服务端无状态化
  • Token的安全性比Session要好,每个请求都会对签名解密,能够防止监听以及重复攻击,而Session必须依赖链路层保障通讯安全
  • 二者不冲突,可以同时存在

为什么从Cookie转到Token

​ 原因在于之前,JQuery前端时代是前后端不分离的,后端在模板渲染前会判断路由是否有权限来决定是否跳转。登录的时候,后端只需要设置SetCookie这个响应头,Cookie就能够存储起来并且自动发送。

​ 但是现在的前后端分离,通常需要进行跨域操作,此时Cookie就有更多的限制了,因此我们更愿意使用手动管理权限的Token方式。

4. JWT

JWT原理是在服务器认证后,生成一个JSON对象,发送给用户,这个JSON对象包含用户的信息,之后用户每次发送请求的时候,都需要携带上JWT,为了防止数据被篡改,服务器在生成对象的时候,会加上签名。

4.1 JWT的数据结构

Header.Payload.Signature三部分用.链接

img

  • Header

    1
    2
    3
    4
    {
    "alg": "HS256",
    "typ": "JWT"
    }

    alg表示签名的算法HMAC SHA256typ表示该token的类型,JWT统一为JWT

  • Payload

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "iss (issuer)""签发人"
    "exp (expiration time)""过期时间"
    "sub (subject)""主题"
    "aud (audience)""受众"
    "nbf (Not Before)""生效时间"
    "iat (Issued At)""签发时间"
    "jti (JWT ID)""编号"
    }

    除了上面7个官方字段外,还可以定义一些私有字段。

  • Signature

    指定密钥(secret)后,按下面的公式生成。

    signature = HMACSHA256(base64UrlEncode(Header)+"."+(base64UrlEncode(Payload), secret)

  • BASE64URL算法

    由于JWT作为令牌可能需要放在URL中,因为+/=在URL中有特殊的含义,所以要被替换掉,具体算法: =省略、+换位-/替换为_

4.2 JWT的使用

  1. 当客户端收到服务端返回的JWT的时候,可以存储在CookielocalStorage中,在之后的每一次请求都需要带上JWT。当JWTCookie中的时候将自动发送(不允许跨域),所以一般会放在HTTP的请求头Authorization中。

    一种使用方法:Authorization: Bearer + 'JWT'(调用OpenAI API的时候就是将api_key放在请求头中)

    另一种:跨域的时候,JWT放在POST请求的BODY中。

4.3 JWT的特点

  1. JWT默认不加密,但是可以生成原始Token后,使用密钥进行加密。
  2. JWT不加密的时候,由于里面的数据是可以被解码的,可以被直接获取,所以不应该在JWT内写入秘密数据,并且永远使用强密码哈希算法处理密码。
  3. JWT除了用于认证外,也可以用于信息的交换,可以减少数据库的查询次数。
  4. JWT的最大缺点在于,服务端并不会保存Session信息,因此无法在过程中废除Token或者更改JWT的权限。就是说,JWT一旦签发,在到期前都始终有效,除非服务器部署额外的逻辑。
  5. JWT本身包含了认证信息,如果泄露,那么任何人都可以获取该令牌的权限。为了减少盗用,JWT的有效期一般被设置的很短,对于一些重要的信息,使用的时候应该再次对用户认证。
  6. 为了减少盗用,JWT不应该使用HTTP传输,而是应该使用HTTPS

TOKEN与JWT的区别

  • 相同:
    • 都是访问资源的令牌
    • 都可以记录用户的信息
    • 都使服务器无状态化
    • 都是只有验证成功后,客户端才能访问服务端上的受保护的资源
  • 不同:
    • Token:服务端验证客户端发送的Token的时候,还需要查询数据库获取用户的信息,并且验证Token是否有效。
    • JWT:Payload与Token加密后存储在客户端中,服务端只需要使用密钥解密并检验(JWT自己实现)即可,不需要或较少查询数据库,因为JWT包含了用户信息与加密的数据。