当前位置: 首页 > 后端技术 > Node.js

多站点单点登录实现方案

时间:2023-04-03 23:39:02 Node.js

今天写了一篇关于多域名下单点登录实现的文章。有这样一个场景,公司有多个不同域名的站点。我们希望用户登录任意一个站点,打开其他站点进行登录,这个过程就是单点登录。由于多个站点使用相同的用户系统,单点登录可以避免用户重复登录,使用户在站点之间更顺畅地切换,甚至在没有意识的情况下。单点登录需要实现的是,一个站点登录后,其登录状态会同步到其他几个站点。让我们把它分成两部分。先说单站的登录过程,再说同步登录状态的过程。登录相关架构服务器使用nodejs,缓存使用redis。用户登录凭据是使用基于会话的cookies来维护的,使用cookies作为登录凭据是目前主流的方式。session信息由redis承载。从数据层面来说,redis中存储session对象的key是cookie中的valuekey,它是由UUID生成的唯一标识。为了保证session和cookie保持对应,session对象的创建和修改会触发服务器去浏览器写cookie登录流程。我们先来看单个站点的登录流程。当用户第一次打开站点时,服务器会生成一个会话对象。此时session中没有用户信息,服务器向浏览器写入cookie。用户触发登录操作。处理完带校验参数的登录逻辑,生成用户信息,将用户信息写入session对象,更新缓存redis。我们画个图,如下同步登录状态。由于登录状态是由cookie和session决定的,而cookie是由session写入的,也就是说只要session同步到其他站点,其他站点只要创建或更新域名即可他们得到会话cookie,使不同域名下的两个站点具有相同的登录信息。所以同步登录状态其实就是一个session怎么同步的问题。而我们的session是使用redis作为载体的,那么其他站点只要能够获取到redis中存储的用户信息,不就可以创建自己的session对象了吗?这是正确的!如何同步session的问题就变成了如何让其他站点从redis获取用户信息,即如何让其他站点知道存储用户信息的rediskey。至此,我们需要解决的问题就很明显了:如何在不同站点之间传输用户凭证。为了描述方便,我们假设有两个站点,分别是A站点和B站点。由于A站点和B站点的域名不同,基于同源策略无法共享cookie,所以我们主动通过接口请求并传递用户的唯一凭证。一般过程如下。登录逻辑完成后,站点A将用户凭据返回给浏览器。为了安全起见,在传输凭证之前,凭证被加密。站点AES或RSAA客户端获取凭据后,调用站点B提供的同步登录态接口,将凭据传递给站点B服务器获取凭据,解密,查询缓存中的用户信息,创建一个session对象,并在B站点的域名下写入cookie信息,同步B站点的登录状态。基于上图,我们改进了同步时序图。上面同步登录状态的场景描述了用户第一次登录时的同步过程。还需要考虑其他情况。比如B站点获取到的登录状态是无效的。这时候访问B站页面,需要去A站同步登录状态一次。B站点有两种页面,一种需要登录才能访问,另一种不需要登录就可以访问。第一种情况,你需要重定向到A站点,但是为什么要回到A站点呢?因为此时我们不知道A站点的登录状态是否也是无效的,所以我们需要回到A站点来判断A站点当前的登录状态。如果A站点的登录状态也是无效的,然后去登录页面重新登录。SiteA有登录状态,所以只需要执行同步登录状态的操作即可。第二种情况,虽然B页面可以看到没有登录状态,但是企业网站往往会在页面的head部分标记用户的登录状态,所以为了让这部分正常显示,我们在当前页面更新登录状态异步状态即可。如果还有其他场景,处理逻辑和这个类似,本质无非就是获取一次证书,重新更新站点缓存。因为跨域请求需要在A站点请求B域名下的接口,所以会出现跨域问题。跨域问题的常见解决方案如下。JSONP是一种很常见很普遍的方式。Image使用Imagesrc绕过同样的Source策略,所以构造一个Image发送给request也是可行的,服务端不需要做太多的修改。旧浏览器不支持CORS。服务器端需要设置Access-Control-Allow-Origin,允许任意域或指定域发起的请求从当前服务器获取数据。总结文章以plan为主,详细的代码就不放了。其实有了具体的plan,代码的实现就不难了。多站点单点登录肯定还有其他优秀的解决方案。欢迎小伙伴们在评论区讨论~