登陆鉴权方案设计详解

383 阅读4分钟

我正在参加「掘金·启航计划」

鉴权

大概流程

通常我们去构建以Google登陆的系统时,我们的流程大致为

  1. 客户端唤起登陆窗口向Google Oauth2.0服务发起请求获取一个凭证Code
  2. 客户端获取Code后发送给服务端,服务端通过请求Google People 服务获取用户信息
  3. 服务端通过用户信息请求权限服务,获取当前用户的权限列表
  4. 将用户信息和权限信息返回给客户端

具体方案

但是这里会存在一个问题,如果是初次登陆时,这样的流程是可以满足需求的。但是,我们每次去请求业务数据的时候,我们需要知道,这个请求是否在之前已经完成登陆。

我们都知道,HTTP的请求是无状态的,如果需要让用户变得有状态,我们需要添加额外的请求头

  1. Cookie
  2. Authentication

Cookie方案

流程

HTTP Cookie是服务端发送到用户浏览器并保存在本地的一块数据,它会在浏览器下次向同一服务器再次发起请求时带上发送到服务器,验证登陆状态。

具体流程:

  1. 客户端发送登陆请求,服务端返回Cookie信息
  2. 客户端再次发送请求,服务端通过Cookie获取存储的Session信息

问题

我们发现,这个方案如果是单服务节点的情况下,是可以满足基本的需求的,但是服务节点>1的时候,我们可能通过Cookie获取不到存储在服务端本地的信息。所以我们可能需要引入共享缓存的方式去解决这个问题。

Token方案

方式

Token的方案流程基本上和Cookie一致,这里可能存在两种选择。

  1. 直接使用Google People放回的Token
  2. 使用JWT生成Token

Google Token和JWT的对比

Google Token

但是Google People生成的Token只是一个基本的随机字符,并不会携带用户信息。所以依然需要通过Token再向服务请求用户数据,然后通过用户数据请求权限服务,获取权限点。在用户退出登陆时,可以通过revokeToken的方式使Token失效。

JWT Token

使用JWT的方案可以将用户信息和权限信息生成对于的Token,用户下次访问时,可以通过Token直接去解析成相对应地用户信息和权限信息。但是JWT的弊端就是,如果用户登出,当前的Token在未过期前依然有效

结论

在这两种方案中,第一种会过于依赖于Google服务和权限服务的稳定性,如果某个服务宕机会使本身服务不可用。所以在业务背景下可以忽略登出导致Token依然有效的前提下,使用第二种方案是不错的选择。

Token在登出无效化

上面说了,如果需要考虑在多服务节点、并且是有效登出的情况下,除了在基于Google revokeToken之外,Token方案需要引入共享缓存服务器来确保Token失效。具体的方式这里以Redis为例。

流程

  1. 登陆时将用户信息部分解析成Token,将用户信息和权限信息以Token为key保存至Redis
  2. 用户再次登陆,通过token从Redis获取用户和权限信息作为鉴权的依赖
  3. 业务数据返回

当用户登出时,删除Redis里的token。用户下次访问时,从Redis里获取不到相应的数据,提示用户已登出,鉴权失败。

问题

我们发现,这样的方式似乎已经解决了基本的业务场景。可是如果Redis在一定时间内不能使用了,那么我们的系统也会处于不可用的状态,即使登陆上去了,用户在对业务接口进行鉴权是,由于访问不到Redis数据也会提示用户已登出

方案

当然我们可以监控Redis的状态,当Redis处于不可用的时候,默认放开鉴权或者重新请求数据也是一种处理方式。但是同时,我们也可以使用双重缓存的机制来保证系统的可用性。

总结

其实就是在中间加了一层本地缓存,我们在去请求Redis之前先请求本地的Cache,如果命中了,那么就可以使用本地缓存去做鉴权处理。这种处理并不能完美解决Redis在宕机的情况下多服务节点的问题,主要目的相比之前的方案,系统的可用性会有一定的提高。

木更

2022.09.21