我正在参加「掘金·启航计划」
鉴权
大概流程
通常我们去构建以Google登陆的系统时,我们的流程大致为
- 客户端唤起登陆窗口向Google Oauth2.0服务发起请求获取一个凭证Code
- 客户端获取Code后发送给服务端,服务端通过请求Google People 服务获取用户信息
- 服务端通过用户信息请求权限服务,获取当前用户的权限列表
- 将用户信息和权限信息返回给客户端
具体方案
但是这里会存在一个问题,如果是初次登陆时,这样的流程是可以满足需求的。但是,我们每次去请求业务数据的时候,我们需要知道,这个请求是否在之前已经完成登陆。
我们都知道,HTTP的请求是无状态的,如果需要让用户变得有状态,我们需要添加额外的请求头
- Cookie
- Authentication
Cookie方案
流程
HTTP Cookie是服务端发送到用户浏览器并保存在本地的一块数据,它会在浏览器下次向同一服务器再次发起请求时带上发送到服务器,验证登陆状态。
具体流程:
- 客户端发送登陆请求,服务端返回Cookie信息
- 客户端再次发送请求,服务端通过Cookie获取存储的Session信息
问题
我们发现,这个方案如果是单服务节点的情况下,是可以满足基本的需求的,但是服务节点>1的时候,我们可能通过Cookie获取不到存储在服务端本地的信息。所以我们可能需要引入共享缓存的方式去解决这个问题。
Token方案
方式
Token的方案流程基本上和Cookie一致,这里可能存在两种选择。
- 直接使用Google People放回的Token
- 使用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为例。
流程
- 登陆时将用户信息部分解析成Token,将用户信息和权限信息以Token为key保存至Redis
- 用户再次登陆,通过token从Redis获取用户和权限信息作为鉴权的依赖
- 业务数据返回
当用户登出时,删除Redis里的token。用户下次访问时,从Redis里获取不到相应的数据,提示用户已登出,鉴权失败。
问题
我们发现,这样的方式似乎已经解决了基本的业务场景。可是如果Redis在一定时间内不能使用了,那么我们的系统也会处于不可用的状态,即使登陆上去了,用户在对业务接口进行鉴权是,由于访问不到Redis数据也会提示用户已登出
方案
当然我们可以监控Redis的状态,当Redis处于不可用的时候,默认放开鉴权或者重新请求数据也是一种处理方式。但是同时,我们也可以使用双重缓存的机制来保证系统的可用性。
总结
其实就是在中间加了一层本地缓存,我们在去请求Redis之前先请求本地的Cache,如果命中了,那么就可以使用本地缓存去做鉴权处理。这种处理并不能完美解决Redis在宕机的情况下多服务节点的问题,主要目的相比之前的方案,系统的可用性会有一定的提高。
木更
2022.09.21