用户在完成扫码登录后,应当返回一个登录凭证给前端保存,以方便下一次登录时不需要重复“扫码 => 登录”这个麻烦的步骤,而这个登录凭证(Token)一般就由服务器生成并返回。

Token 生成方式

Token 的生成方式主要有以下几种:

  1. 随机字符串:可以使用一些随机数生成算法,如 :UUIDSnowflake 等来生成一个随机的字符串作为 Token。由于随机字符串本身就是随机分布的,因此具有很高的安全性。
  • JWT(JSON Web Token):JWT 是一种基于 JSON 格式的开放标准(RFC 7519),用于在多方之间安全地传输信息。它将用户身份信息和权限等相关信息编码成一个 JSON 对象,并通过数字签名或者加密等方式进行验证和保护。JWT 除了可以用于 Token 登录外,还可以用于 API 认证、单点登录等场景。
  • SessionID

通常的 Token 在服务器端的实现方式有这几个:

  1. 用 SessionID 充当 Token。
  2. 使用 Json Web Token (JWT)
  3. 中心化存储 Token

下面就来介绍一下各个方案的优缺点:

Cookie + Session 登录

我们知道 HTTP 请求是一种无状态的“请求-应答”模型。

无状态(Stateless) 指的是系统或协议在每次请求处理时,不需要记住先前的任何状态或上下文信息。换句话说,每次请求都是独立的,系统不依赖于之前的任何请求数据

尽管 HTTP 协议是无状态的,但在实际应用中,许多情况下需要“记住”用户的身份信息或会话状态。就拿最常见的我们在网页上登录一个账号,后续的操作都是基于这个账号的,其实这就打破了 HTTP 无状态。

为了解决这种无状态,就出现了 Cookie + Session 方案:

image-20241201164450307

  1. 用户在登录后,服务器会创建一个 Session,Session 是一种服务端保存会话的机制。
  2. 创建后返回一个 SessionID 给前端,前端将其通过 Cookie 进行保存。
  3. 在后续的每次请求中,前端都会在请求头中带上这个 Cookie(值等于 SessionID),服务端对 Cookie 进行验证,将其在 Session 中查询,如果查询到就说明认证成功,如果未查询到就通知前端进行删除。

优点

cookie 的优点在于前端”无感知“,因为这是 http 协议约定好的内容,并且无需额外开发。

image-20241201164505299

缺点

缺点也很明显,首先:

  1. 跨域问题:浏览器发送请求到不同的域时(即跨域请求),浏览器不会自动将该域下的 Cookie 发送到目标服务器(可以通过 CROS 等跨域方法解决)。
  2. 单机扩展性问题:Session 存储在服务端上,当采用集群模式时,各个服务器的 Session 不共享。
  3. 一些设备会禁止 Cookie:归根到底这个方案是基于 Cookie,如果用户不同意设置 Cookie,方案就无效了。

总结

  1. SessionID 其实就充当了 Token 的功能,只需要认证 SessionID 是否存在即可。
  2. 该方案适合于单机小项目,简单方便,无需额外开发。

JWT 实现 Token

JWT(JSON Web Token)简单说就是通过 可逆加密算法 生成一串包含用户信息、过期时间等的 Token 返回给前端进行保存。JWT 最大的特点就是服务端不保存数据,只要服务端拿到这串 Token,使用相同的加密算法进行解密,就能辨别 Token 是否有效。

优点

JWT 最大的特点就是 服务端不保存会话状态,JWT Token 无论哪个服务器拿到,只要解密出来就能使用,这极大程度的省掉了服务器的性能损耗。

缺点

  1. 无法主动更新:Token正式 JWT 最大的特点,导致其只要签发出去就不受服务器控制,只能等待其自行过期。比如在其有效期间,如果被盗号,盗号者就能拿着这串 Token 随便用,服务器也无法管制。
  2. 当然可以通过黑名单的方式将这串 Token 保存进去,服务器只要发现是这个 Token 就不允许访问。但是这样又引入了额外的服务端存储机制,与 JWT 的去中心化特性恰恰相反。

总结

由于 JWT 的无状态、去中心化,只要其签发出去,无法使其主动失效,也无法进行续期,只能等待其时间过期后重新签发。

但好处也很明显,去中心化无需服务端额外存储,不需要管理,也省去了服务器的性能消耗。

JWT 双 Token 方式

双 token 的方式主要解决 jwt 无法续期的问题。由于 jwt 的特性,意味着只要签发出去在一定时间能就能通行:

  1. 有效时间过长:服务器对其掌控能力很弱,在有效期内出现任何问题服务器都没法进行管理,别人盗走 token 就会有很大风险。
  2. 有效时间过短:用户需要频繁的登录认证,体验感很差。

而双 Token 采用一次签发两个 token 为一组:

  1. access_token:该 token 有效时间较短,与服务器认证的时候会提交此 token。
  2. refresh_token:该 token 有效时间设置一般较长,通常为一星期,当发现 access_token过期后,会通过这个 token 请求服务器刷新 access_token,在这个过程中就可以对用户状态进行检查等。

总结

双 Token 的方式相比于前面就比较完美了,通过较短的 access_token 降低了出问题的风险,又通过长时间的 refresh_token 实现了续期,增加了用户的体验感。

中心化管理 Token

在上述的 Cookie + Session 方案上,我们可以通过中心化方案替代 Session,实现分布式多机扩展。最常见的方案就是将 Token 保存在 Redis 上。

  1. 需要实时管理用户状态,比如用户主动登录注销、管理员强制下线、登录续期等功能。
  2. Redis 作为中心存储有较好的可用性。

Token 生成方式

一般来说采用 uuid(随机生成一串字符作为 Token 保存),但 uuid 有撞库的风险,因此还可以加上一个 uid 作为辅助条件,前端同时发送 uuid + uid,Redis 通过 uid 作为 key,uuid 作为 value。撞库需要同时满足两个条件才能成功。

而既然需要同时发送随机 uuid + uid 用户信息等参数,干脆直接用 jwt 打包在一起生成一传字符串作为 token 传输,Redis 进行保存即可,只不过服务器要多一次解析 jwt token,会消耗一些性能。但这样前端传输就少了一个参数,更加简单一些(自行取舍)。