Token 认证技术方案
用户在完成扫码登录后,应当返回一个登录凭证给前端保存,以方便下一次登录时不需要重复“扫码 => 登录”这个麻烦的步骤,而这个登录凭证(Token)一般就由服务器生成并返回。
Token 生成方式
Token 的生成方式主要有以下几种:
- 随机字符串:可以使用一些随机数生成算法,如 :UUID、Snowflake 等来生成一个随机的字符串作为 Token。由于随机字符串本身就是随机分布的,因此具有很高的安全性。
- JWT(JSON Web Token):JWT 是一种基于 JSON 格式的开放标准(RFC 7519),用于在多方之间安全地传输信息。它将用户身份信息和权限等相关信息编码成一个 JSON 对象,并通过数字签名或者加密等方式进行验证和保护。JWT 除了可以用于 Token 登录外,还可以用于 API 认证、单点登录等场景。
- SessionID。
通常的 Token 在服务器端的实现方式有这几个:
- 用 SessionID 充当 Token。
- 使用 Json Web Token (JWT)
- 中心化存储 Token
下面就来介绍一下各个方案的优缺点:
Cookie + Session 登录
我们知道 HTTP 请求是一种无状态的“请求-应答”模型。
无状态(Stateless) 指的是系统或协议在每次请求处理时,不需要记住先前的任何状态或上下文信息。换句话说,每次请求都是独立的,系统不依赖于之前的任何请求数据
尽管 HTTP 协议是无状态的,但在实际应用中,许多情况下需要“记住”用户的身份信息或会话状态。就拿最常见的我们在网页上登录一个账号,后续的操作都是基于这个账号的,其实这就打破了 HTTP 无状态。
为了解决这种无状态,就出现了 Cookie + Session 方案:
- 用户在登录后,服务器会创建一个 Session,Session 是一种服务端保存会话的机制。
- 创建后返回一个 SessionID 给前端,前端将其通过 Cookie 进行保存。
- 在后续的每次请求中,前端都会在请求头中带上这个 Cookie(值等于 SessionID),服务端对 Cookie 进行验证,将其在 Session 中查询,如果查询到就说明认证成功,如果未查询到就通知前端进行删除。
优点
cookie 的优点在于前端”无感知“,因为这是 http 协议约定好的内容,并且无需额外开发。
缺点
缺点也很明显,首先:
- 跨域问题:浏览器发送请求到不同的域时(即跨域请求),浏览器不会自动将该域下的 Cookie 发送到目标服务器(可以通过 CROS 等跨域方法解决)。
- 单机扩展性问题:Session 存储在服务端上,当采用集群模式时,各个服务器的 Session 不共享。
- 一些设备会禁止 Cookie:归根到底这个方案是基于 Cookie,如果用户不同意设置 Cookie,方案就无效了。
总结
- SessionID 其实就充当了 Token 的功能,只需要认证 SessionID 是否存在即可。
- 该方案适合于单机小项目,简单方便,无需额外开发。
JWT 实现 Token
JWT(JSON Web Token)简单说就是通过 可逆加密算法 生成一串包含用户信息、过期时间等的 Token 返回给前端进行保存。JWT 最大的特点就是服务端不保存数据,只要服务端拿到这串 Token,使用相同的加密算法进行解密,就能辨别 Token 是否有效。
优点
JWT 最大的特点就是 服务端不保存会话状态,JWT Token 无论哪个服务器拿到,只要解密出来就能使用,这极大程度的省掉了服务器的性能损耗。
缺点
- 无法主动更新:Token正式 JWT 最大的特点,导致其只要签发出去就不受服务器控制,只能等待其自行过期。比如在其有效期间,如果被盗号,盗号者就能拿着这串 Token 随便用,服务器也无法管制。
- 当然可以通过黑名单的方式将这串 Token 保存进去,服务器只要发现是这个 Token 就不允许访问。但是这样又引入了额外的服务端存储机制,与 JWT 的去中心化特性恰恰相反。
总结
由于 JWT 的无状态、去中心化,只要其签发出去,无法使其主动失效,也无法进行续期,只能等待其时间过期后重新签发。
但好处也很明显,去中心化无需服务端额外存储,不需要管理,也省去了服务器的性能消耗。
JWT 双 Token 方式
双 token 的方式主要解决 jwt 无法续期的问题。由于 jwt 的特性,意味着只要签发出去在一定时间能就能通行:
- 有效时间过长:服务器对其掌控能力很弱,在有效期内出现任何问题服务器都没法进行管理,别人盗走 token 就会有很大风险。
- 有效时间过短:用户需要频繁的登录认证,体验感很差。
而双 Token 采用一次签发两个 token 为一组:
access_token
:该 token 有效时间较短,与服务器认证的时候会提交此 token。refresh_token
:该 token 有效时间设置一般较长,通常为一星期,当发现access_token
过期后,会通过这个 token 请求服务器刷新access_token
,在这个过程中就可以对用户状态进行检查等。
总结
双 Token 的方式相比于前面就比较完美了,通过较短的 access_token
降低了出问题的风险,又通过长时间的 refresh_token
实现了续期,增加了用户的体验感。
中心化管理 Token
在上述的 Cookie + Session 方案上,我们可以通过中心化方案替代 Session,实现分布式多机扩展。最常见的方案就是将 Token 保存在 Redis 上。
- 需要实时管理用户状态,比如用户主动登录注销、管理员强制下线、登录续期等功能。
- Redis 作为中心存储有较好的可用性。
Token 生成方式:
一般来说采用 uuid
(随机生成一串字符作为 Token 保存),但 uuid
有撞库的风险,因此还可以加上一个 uid
作为辅助条件,前端同时发送 uuid + uid
,Redis 通过 uid
作为 key,uuid
作为 value。撞库需要同时满足两个条件才能成功。
而既然需要同时发送随机 uuid + uid 用户信息等参数,干脆直接用 jwt 打包在一起生成一传字符串作为 token 传输,Redis 进行保存即可,只不过服务器要多一次解析 jwt token,会消耗一些性能。但这样前端传输就少了一个参数,更加简单一些(自行取舍)。