V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
bear2000
V2EX  ›  程序员

单点登录(SSO)

  •  
  •   bear2000 · 2022-02-01 15:52:54 +08:00 · 5254 次点击
    这是一个创建于 1020 天前的主题,其中的信息可能已经有所发展或是发生改变。

    求问万能 V 友,哪篇文章,或者哪个视频讲解单点登录( SSO )讲得比较通俗易懂?

    12 条回复    2022-02-02 08:02:39 +08:00
    ji39
        1
    ji39  
       2022-02-01 19:11:29 +08:00
    铜球,
    Ptu2sha
        2
    Ptu2sha  
       2022-02-01 19:34:30 +08:00
    如果我没理解错的话 可以看 Ucenter 的源码 没什么难点 P3P 协议兼容 IE 跨域种 cookie 而已
    ZSeptember
        3
    ZSeptember  
       2022-02-01 21:43:33 +08:00   ❤️ 1
    SSO 其实很简单,我来尝试解释下,不讲具体协议,就讲一下大致原理和认证过程

    先定义下参与方,前后端都接入了 CAS 认证过程:

    AppA:*.appa.com
    AppB:*.appb.com
    CAS:*.cas.com 单点登录用户中心



    一个用户进入 AppA ,发现没有登陆,跳转到 CAS 登陆,CAS 登陆以后,在 CAS 域名下保存 cookie ,然后将 cookie 放在 URL 上重定向到 AppA ,AppA 识别到 cookie ,将 cookie 保存到 AppA 域名下,下次就不需要跳转了,直接就是登陆过了。

    同一个用户进入 AppB ,发现没有登陆,跳转到 CAS ,CAS 已经有 cookie 了,直接带着 cookie 重定向到 AppB 就可以了。
    iQXQZX
        4
    iQXQZX  
       2022-02-01 22:02:27 +08:00
    phpfpm
        5
    phpfpm  
       2022-02-01 22:07:26 +08:00
    @ZSeptember 但是实际上,带什么样的 cookie 过去,是 sso 设计的一个核心点和难点。 。。
    ZRoger
        6
    ZRoger  
       2022-02-01 22:24:33 +08:00
    InDom
        7
    InDom  
       2022-02-01 22:42:05 +08:00
    带 Cookies 过去也是不安全的,作为 CAS 不应该信任 App 端。

    如果是我,我会这样设计:

    AppA 发现未登录跳转到 CAS 页面,
    CAS 登录成功后,将用户名等唯一信息经过加密或签名等方式夹在 QueryString 中返回给 AppA ,
    AppA 从 Query 中取出信息并验证是 CAS 签发的( JWT 似乎也行?)
    AppA 生成自己的用户 Cookies 即可。

    AppB 同样处理,CAS 打开后直接将已经登陆的用户信息带回去即可。
    甚至可以全局使用 Ajax 而不需要跳转(已登录的情况下)即可完成授权。

    各 App 端和 CAS 各自管理自己的 Cookies 信息,不需要相互兼容,只需要能识别 CAS 返回的信息即可。
    Rocketer
        8
    Rocketer  
       2022-02-02 00:25:53 +08:00 via iPhone   ❤️ 11
    进入我的职业范围了,简单说说。

    SSO 的意思是 Single Sign-On ,这里的 Single 既有“一次”的意思,也有“一点”的意思。

    对个人应用来说,一般是为了“一次”,就是只需输入一次账号密码,就可以在多处畅游了。

    对企业应用来说,更多是为了“一点”,也就是集中管理。员工离职以后,企业关联的所有服务就都不能用了。

    在一个 SSO 系统里,分为 ID Provider 和 Service Provider 两个角色,简称 IDP 和 SP 。IDP 就是负责提供登录信息的,SP 就是登录信息的消费者。

    当 SP 需要用户通过 SSO 登录时,就把用户发到 IDP ,IDP 处理完了再把用户发回来,顺便带回登录信息。主流实现有 OAuth2 和 SAML 两种,流程都是上面这样。SAML 还允许 IDP 带着登录信息把用户直接发到 SP ,无需 SP-IDP-SP 这样跳来跳去,所以深受企业用户喜爱。但 SAML 是基于证书的,技术实现比 OAuth2 复杂得多,所以一般个人网站都用 OAuth2 。

    最常见的 SSO 就是各种社交登录,以 Google 为例,流程如下:

    1 、SP 把用户 redirect 到 Google (带着 SP 的信息)
    2 、用户登录 Google (如果已登陆,这一步可以跳过)
    3 、Google 把用户 redirect 回 SP (带着一个 code )
    4 、SP 用这个 code 在后台向 Google 换取用户的信息(唯一 ID 、email 之类的)
    5 、SP 根据从 Google 获得的用户信息让用户使用服务。

    注意:这里没有提及任何 cookie 、session 之类的词汇,因为怎么使用从 Google 获取的用户信息是 SP 自己的事。这些信息存在哪里、怎么存、怎么过期,全看你的需要。

    当用户跳到你的另一个应用时,你可以再来一遍上述流程。一般这是个静默流程,因为用户已经登陆 Google 了,在用户看来就是几个白页跳转了几下,就进入正常使用的页面了。
    Rocketer
        9
    Rocketer  
       2022-02-02 00:29:53 +08:00 via iPhone
    补充一句:Google 还支持 Open ID ,即在上述第 4 步就会返回用户的信息,无需第 5 步。
    jinliming2
        10
    jinliming2  
       2022-02-02 02:02:43 +08:00 via iPhone   ❤️ 5
    对楼上几个回答做个简单补充:
    注:下面提到的安全性都是假设 CAS 服务是安全的,而下面单个 App 可能存在安全漏洞。而降低安全维护成本是指,你只需要重点考虑 CAS 的安全性即可将风险降到最低,漏洞影响不至于扩散到所有业务(但并不是说下属 App 的漏洞就不需要补了)

    @ZSeptember #3 没有提到从 CAS 带回 App 的 cookie 应当如何验证,存在两个问题:
    1. 从安全的角度来说,不应当携带 CAS 的 cookie 回到 App ,因为这个 cookie 一旦泄漏,影响将会直接扩散到整个 CAS 下接入的所有服务,一个 cookie 就可以走遍所有服务了。这增大了安全维护成本,任何一个 App 存在安全漏洞,都会扩大影响到所有其他 App 。
    2. 每个接入的 App 都要对 CAS 的 cookie 信息做兼容,App 后台需要同步存储对应的 cookie key 信息,或者统一向 CAS 请求验证增大单点服务的带宽压力。而如果采用类似 JWT 的方式, 这种方式不仅存在天生的设计缺陷(不支持注销强制过期、续期需要跳回 CAS 来进行等),还需要在每个接入的 App 后台存储 secret key ,和上面一条类似,增大了安全维护成本,一个 App 存在漏洞泄漏了 key ,会直接影响所有 App ;并且要更新 key 也需要统一通知所有接入的 App 进行升级,这个成本太大。

    @InDom #7 提到了 cookie 的安全性问题,并做了改进。但是还是存在一些问题:
    1. 采用将用户名加密或签名带过去的方式,也存在问题,同样也是安全维护成本的问题。如果一个 App 存在安全漏洞,那么当用户登录这个 App 的时候,攻击者就可以立即获取带过来的加密或签名过的用户名,使用这个数据就可以立即解锁所有 App (因为的确是 CAS 进行的加密/签名操作,所以肯定是合法的)。
    2. 使用 JWT (就是做签名)意义不大,即便 JWT 存在过期机制(就是同时签名了个时间戳),但攻击者通常都是使用脚本进行攻击的,拿到 JWT 之后瞬间就可以解锁所有服务,JWT 过期时间定多久都不合适。
    3. 使用 Ajax 不可取。CAS 与 App 的域名肯定不同,这就属于跨域请求了,那么 CAS 的登录 cookie 就属于第三方 cookie 了。目前浏览器在逐步将 cookie 的 SameSite 属性默认值改为 Lax ,也就是不发送第三方 cookie ,那么 CAS 在 Ajax 的情况下永远都是返回未登录。除非你已经部署 https 并且主动在 CAS 上将 cookie 的 SameSite 属性设置为 None ,但这样就会引发跨站攻击。因为 CAS 要允许 App 跨域登录,必然要设置放行所有域名的 CORS 头,那么任何第三方网站就都可以发起对 CAS 的跨站攻击了。除非 CAS 预知所有 App 的域名,做 CORS 拦截,但这样就损失了业务扩展的便利性,新 App 必须提前向 CAS 注册。

    #8 的描述是比较全面的,CAS 跳回 App 时带一个一次性的有时效的 Token ,App 后台向 CAS 查询这个 Token 的有效性并得到用户信息,此时 Token 失效。单个 App 存在漏洞也不会扩散影响到其他 App ,因为 Token 是一次性的,在有漏洞的 App 中用掉了就不能用在其他 App 中了。

    前面两个方案的问题都是出在 CAS 返回的 Token 的有效次数上,在 Token 的有效期内可以任意次用于解锁任意服务,没有办法保证 Token 在使用后立即丢弃。第二个方案可以在加密/签名的信息中增加 App 名称做限制,但也存在伪造的可能性,因为 CAS 和 App 互相不知道提供的 App 名称的真实性,可能是由用户客户端的某个 XSS 脚本提供的。
    而第三个方案则是补上了这些漏洞,由 CAS 和 App 互相在后台再做一次互认来实现。
    liuhouer
        11
    liuhouer  
       2022-02-02 08:02:19 +08:00 via iPhone
    前几天刚刚看了这块,慕课网-java 架构师,技术专家第九章,讲的很清晰,代码也不难,大概过了一下,代码收藏了
    liuhouer
        12
    liuhouer  
       2022-02-02 08:02:39 +08:00 via iPhone
    @liuhouer 第九周
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5787 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 01:46 · PVG 09:46 · LAX 17:46 · JFK 20:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.