帮你理清单点登录十个关键问题

帮你理清单点登录十个关键问题#

PS 本文讲的是 SSO单点登录,不是指用户只能一处登录,把账号顶下线的单设备登录

单点登录系统适用的场景有哪些?#

  • B 端,公司需要一个员工统一登录平台,一个账号登录内部应用、产品后台
  • C 端,用户需要一个账号通行公司下多个产品,比如一个 QQ 可以登录腾讯网、IM、QQ 音乐等

单点登录有哪些协议?#

  • cas
  • oidc
  • saml
  • 其他和自定义协议…
    其中 cas 协议官方服务端实现是 java,对其他语言不友好,比如 php 的 sdk 写的奇烂无比,saml 比较复杂

单点登录大概原理是什么?#

各种协议基本原理都类似

  1. sso 客户端重定向到 sso 服务端,带上客户端站点地址给 sso 服务端用户重定向
  2. sso 服务端重定向 (未登录则先登录) 回 sso 客户端,并且在重定向地址 query 中添加一个与当前用户关联的票据 (cas 为 ticket,oidc 协议为 code)
  3. sso 客户端使用在后端发起 http 请求,用票据向 sso 服务端交换用户信息
  4. sso 客户端在本系统查询用户信息 (不存在则新增),并且写入登录状态

依靠共享 SESSION 实现两个共享登录状态是单点登录吗?#

单点登录一定是异系统,每个子系统都有自己的用户表,而共享 SESSION 往往是一个强关联的系统,甚至就是一张用户表

内部用的 SSO 和 C 端 SSO 设计上有什么区别,是否通用?#

两种场景需求不同,设计上往往有较大差异

举例新员工入职录入 SSO 系统后,内网部署的 gitlab 中是没有员工的系统的,员工首次登录 gitlab 后,gitlab 从 sso 拉取到了员工信息,这没有问题
但是财务发工资的时候却会发现财务系统中没有这个员工的信息,因为员工没有登录过财务系统,甚至财务系统压根没有员工登录的功能,所以,企业内部用的 SSO 一般需要上下游同步功能,比如公司使用了企业微信、丁丁、飞书、ldap,sso 需要把里面员工信息同步过来,同时,新员工录入信息,修改信息后,需要立即同步至公司内其他系统。

而在 C 端,这个需求往往不强烈,举例试想以下腾讯有十数亿用户,如果腾讯新上线了一个小众产品,本身只有几十万用户,但是每天把十几亿不使用这个小众产品的用户修改昵称、头像等事件都同步到这个系统,就显得没有必要。

内部 SSO 除了接入自研应用,还会接入各种商用系统、开源系统、saas 系统, 这些系统往往由不同厂商开发,五花八门,所以内部用的 sso 往往必须支持 cas 和 oidc,几行配置就可以用员工账号登录这些系统,而 c 端往往只使用一种统一的协议,方便实现和维护

OAuth2 和 OIDC 区别是什么,OAuth2 能不能用来做 SSO?#

  • code 交换 token 时,OIDC 比 OAuth2 多返回了一个 jwt 格式 id_token,access_token 用于授权,id_token 用于认证。

  • OAuth2 技术上可以搞成 sso,很多人也确实在这样做,但不该!!
    OAuth2 的目的是授权,比如你微博授权登录某站,某站就能代替你发微博
    电商网站 OAuth2 授权给第三方工具,第三方工具就可以通过 OAuth2 代替你完全某些工作,比如上货,整理商品。
    这是 OAuth2 设计出来的主要目的,把你在本站本身具有的功能权限授权给第三方。

  • 你应该使用 OIDC 来代替 OAuth2,从 id_token 中解析用户信息,和 sso 客户端中的用户信息关联,完成登录,而不是通过 oauth2 的 access_token 再去拉取一次用户信息

  • 如果你接入过微信,就会发现腾讯为了防止你获得用户关系链,不同 appid 通过微信 OAuth2 获取到同一个微信用户的 openid 是不同的,oauth2 做 sso 会有很多潜在问题,尤其是同步退出问题

怎么同步退出所有站点?#

举例 cas 协议中,sso、 client1、 client2 都处于登录状态,点击 client1 退出重定向指 sso 退出,此时 client2 还是登录状态

  • 方案 1: 异步通知退出
    sso 服务端退出时,启动一个异步队列任务,向当前登录用户当前登录 session 发放过 ticket 的 cas client 发送 http 通知,携带 ticket,sso 客户端收到通知后,找到使用使用该 ticket 登录的用户 session_id,使 session_id 失效

大致原理如此,如果是自定义协议可以直接在或者票据或者或者用户信息时,携带 session_id 给 sso 服务端,sso 服务端退出时直接 http 通知 sso 客户端把该 session_id 失效即可
sso 服务端可以在 redis 存一个含当前 session_id 的 hash,hash 里存 sso client_id 科 client session_id 即可,退出时把这个 hash 拉出来通知即刻

不过不推荐这个方案,略复杂、可靠性不高

  • 方案 2: 同步通知退出

      sso退出时,直接遍历请求sso client的退出页面即可,js也行,iframe也行
      这个方案也是大厂都在用的方案,比如新浪家退出是js请求https://login.sina.com.cn/sso/logout.php,返回一个数组
      ```
          [
            "https://passport.weibo.com/wbsso/logout",
            "https://passport.krcom.cn/sso/crossdomain?action=logout&entry=krvideo",
            "https://passport.sina.cn/sso/crossdomain?action=logout",
            "https://passport.weibo.cn/sso/crossdomain?action=logout"
          ]
      ```
      然后js轮流请求返回的这些退出页面实现退出
    
      小米退出则是跳转至https://account.xiaomi.com/pass/logout,然后把各个域名下的退出页面作为图片调用
      https://logout.xiaomi.net/
      https://logout.xiaomi.cn/
      https://logout.miui.com/
      https://logout.xiaomi.com/
      https://logout.mi.com/
      https://logout.duokan.com/
      https://logout.miwifi.com/
      https://logout.mipay.com/
      https://i.mi.com/logout
      https://credit.mixiaojin.com/gw/mixiaojin/logout

如果一个页面不需要登录也可以访问,即不会强制跳转,如何从 sso 获得登录信息?#

比如 cas 是需要跳转 cas/login 才能拿到票据交换用户信息的,如果一个 to c 的网站,用户不登陆就可以浏览新闻,此时新闻站不知到 sso 登录,也不会强制跳转到 sso 去

这种情况在 B 端不是问题,毕竟内部系统往往都需要登录,或者就让他自己点以下跳转,在 C 端体验就比较差了,
常见方案是 sso 登录时除了 session_id,还有另外在根域名下写一个 cookie,记录自己已经处于登录状态,新闻站读取到这个 cookie,
知道 sso 已经登录则与 sso 交互进行登录,如果没有这个 cookie,则继续游客状态,cookie 可以加密一下防止篡改

如果你的站点处于多个根域名下,则使用上面同步退出的方案,登录时请求多个域写登录状态标识,伪例子:

你在 account.taobao.com/login 登录,成功后这个页面 js 调用

我不想跳转登录,想弹窗登录如何做?#

cas和oidc都是跳转登录的,你可以像百度和微博一样,自定义协议实现

比如从百度首页,弹窗登录后返回一个链接数组,js依次请求
```
https://baike.baidu.hk/common/api/crossdomain?bdu={bdu}&t={timestamp}
https://www.yoojia.com/sync/crossdomain?bdu={bdu}&t={timestamp}
https://user.hao123.com/static/crossdomain.php?bdu={bdu}&t={timestamp}
```
其中bdu是加密票据,crossdomain页面解密这个bdu之后,根域名下写入名为BDUSS的cookie

任意一个百度站点,如贴吧检测到BDUSS这个cookie后,拿BDUSS在发送后端请求向https://passport.baidu.com交换用户信息,通过唯一id查询用户
如果贴吧没有这个用户写入用户表后再登录,有这个用户则直接登录,写入贴吧站自己的session_id

如果贴吧自己的session_id还处于登录状态,但是BDUSS没有了 说明百度的sso(passport.baidu.com)已经退出了,则贴吧也退出自己的账号系统即可

重点: 百度和新浪均为这个方案,但是有缺陷的,比如你在登录后把新浪网的cookie清空,此时你就会发现新浪退出了,非同域的微博却还登录着的,除了再输密码登录一次别无它法,而必须跳转获取票据的方案均不存在该问题。
重点2:由于chrome更新了cookie策略,跨域设置cookie必须samesite设置成none,而samesite的前提是cookie设置secure,即只有https可以读取,所以除非你的所有站点都使用https,否则最好使用跳转方案

如何同步用户信息?#

sso除了登录名、密码、往往还集中储存头像、邮箱、手机号等用户信息
假设贴吧登录状态是一个月,但是头像在拿BDUSS向passport.baidu.com交换用户信息时就已经拿到了,如果不退出的话贴吧和passport.baidu.com在这一个月内也不会再交互
如果用户修改了头像,贴吧会一直是旧头像,除非退出重新登录拉取

在cas、oidc中都没有关于这部分的定义,因为他们只管认证,所有用户资料的同步不管你是使用cas、oidc还是自定义协议实现的sso,都要自己设计同步方案。

可以客户端主动拉取也可以sso主动推送,最好每个sso客户端站点都可以独立配置自己要订阅的事件和信息变更,没有必要做全量推送。
本作品采用《CC 协议》,转载必须注明作者和本文链接
:blush: wink
本帖由系统于 10个月前 自动加精