单点登录详细流程
单点登录其实分两类。
第一类,同一个站点。因为集群部署,需要实现单点登录。不会因为请求被转发到不同的服务器,而导致需要重新登录。
这一类基本所有的互联网企业都需要解决,因为已经很少有一台服务器就能抗住的互联网应用场景了。其解决方案多样,有以下几种常用方法:
1.通过ip进行负载均衡。使同一ip的请求均分发到同一台服务器。可以维持登陆状态。但是如果访问可能ip会变,则需要多次登陆
2.将登录状态存放在redis中。ip虽然会变,但是cookie中的sessionID在失效前不会变,不同的服务器拿到相同的sessionID,去redis中查找,可以维持登录状态
3.微服务下,用户登录状态下都通过调用远程服务获取。登录时分配唯一token存在cookie中。每次访问都调用用户服务查看登录信息。
第二类,同一家公司,或几家合作公司,想要共享用户及登录状态(注意,不是auth2.0这种授权登录。授权登录流程不在本文讨论范围内)。比如www.taobao.com与www.tmall.com,共享登录状态。在taobao登录后,输入www.tmall.com,进入后仍然有用户名及登录信息。因为跨域了,所以之前将token或sessionID存在发cookie中的做法便行不通了。
第二类相比第一类,有相同问题的互联网企业少很多。所以网上能找到的解决方案少,而且都很简单:
1.用jsonp进行跨域获取另一站点的cookie值。据说taobao和tmall用的是这种方式。
2.有一个共同站点,所有要共享登录状态的站点的登陆页面都是统一的SSO站点。如果需要登录则跳转至SSO站点,登录成功后或已登录,SSO站点再带上token返回原站点
考虑到跨域相关的安全性问题不甚了解,而且粗略考虑了下,跨域的方式,两个站点还好,相互取cookie。如果多站点,每个站点都要去所有的站点尝试取cookie信息,又难维护又影响性能(对这种方式不太了解,如果理解有误欢迎斧正)。如果最终选择了第二种方式。可是第二种方式也有个问题,网上的方案都是实验性质的,都很简单,不完善。只考虑了一方登录,另一方也登录的情况。没考虑退出的情况,也没考虑凭证失效。为了实际落地,设计了如下的方案。可能仍然考虑不足,希望和大家一起探讨。
(1)通过浏览器访问A站点。这次访问后A站点在服务器内存中分配一块空间Session供SessionID 1使用。此时Session中为空
(2)因为需要登录,则跳SSO站点的登录页面。因为跨域,SessionID 1带不过来。生成出了SessionID 2。用户在该页面上登录成功。SSO生成临时token1及自定义登录cookie供获取非临时token,临时token供获取userInfo。在redis中存入如下键值对:自定义cookie-非临时token 临时token1-非临时token 非临时token-userInfo
(3)SSO跳转回原url。并带上?临时token=……的参数。浏览器带着之前的SessionID以及查询参数临时token请求A站点页面
(4)A站点使用临时token1向SSO请求登录用户信息
(5)SSO将临时token1删掉,并返回与之相对的非临时token与userInfo。A站点将非临时token存入SessinID 1对应的Session空间里。
(6)用户访问B站点。因为跨域,所以B站点生成了SessionID 3及相对应的Session空间,亦为空
(7)B站点需要登录,则跳转SSO登录页面。因为之前已经在该域下存在了自定义登录cookie及SessionID 2,访问时会带上这两个值。
(8)SSO根据自定义登录cookie知道用户已经登录且仍然有效。生成临时token2返回原B站点url。
(9)B站点使用临时token2请求SSO
(10)SSO将临时token2删除,并返回与之对应的非临时token及userInfo。B站点将非临时token存入SessionID 3对应的Session中
(11)A站 B站每次均使用非临时token向SSO请求用户登录状态
设计思想相关问答:
问:为什么要需要只使用一次的临时token,不能直接使用非临时token
答:因为token是使用url?的方式传递的,会在浏览器地址栏闪现一下,所以不是很安全,如果传递非临时token,就可以直接请求服务获取用户信息
问:为什么A站 B站不能使用本地的SessionID本地控制登录状态
答:如果仅在A站访问A站退出再访问A站,确实可以本地控制。但是如果A站退出,B站不请求SSO,是无法获知用户已经退出登录的。相当于,只共享了登录状态,没共享退出状态
问:SSO已经有SessionID 2了。为什么还要生成自定义cookie。不直接使用SessionID 2。
答:浏览器和站点A由SessionID 1维系。浏览器和站点B由SessionID 3维系。但是浏览器和SSO站点无法通过SessionID 2维系。因为虽然每次请求站点A/B都会请求SSO查询登录状态,但是都是通过服务调用的方式。无法刷新Session。所以SSO中的Session一段时间后会被清除。在下次需要访问SSO站点登录页面时,服务器会发现SessionID 2对应的Session
已被清除,会重新分配SessionID。这样一切的联系就都断了。虽然A站B站因为持有非临时token,还可以正常访问。但是之前未登录过的C站点D站点就要重新登录了。所以解决方案是将服务器端的Session内存移到Redis中,根据策略失效。用自定义cookie代替SessionID 2。使其不会被服务端替换。
打了好多字,好累……希望大家能看得动,看得懂。有空来调整下格式和颜色,方便阅读