SSO 的概念及实现

2019-01-31 17:52:51   最后更新: 2019-02-10 13:50:44   访问数量:209




SSO 全称 Single Sign On,就是所谓的“单点登录”

对于一个复杂的业务系统来说,其中会有多个应用子系统,每个应用子系统提供独立的功能

而每一个应用子系统都有着登录、验证和鉴权的需求,当用户需要同时使用多个子系统时,就需要分别执行多次登录操作,这无疑是非常繁琐的一件事

是否可以让用户一处登录处处使用呢?这就是 SSO 要解决的问题

 

 

方案1 -- cookie 验证

既然要实现一次登录处处使用,那就要做到用户标记,最简单的做法就是首次登录为用户生成一个标识,例如,该标识存储到客户端缓存,例如浏览器的 cookie 里

当另一个应用也需要验证登录,只要在相同的客户端缓存中读取到标识,服务端验证标识即可

 

 

方案2 -- 统一认证中心

上述方案将 cookie 的生成和校验逻辑分散在各个子系统中,存在下面几个问题:

  1. 维护困难 -- 如果逻辑发生变更,意味着所有子系统必须在同一时间修改和上线,否则会造成用户反复登录失败重新登录的问题
  2. 耦合过强 -- 事实上,各子业务对于用户权限的校验并不关心,这并不是子业务的业务内容,各子系统应高度内聚,服务于自己的业务
  3. 无法处理安全策略 -- 对于用户的权限校验与风控、安全策略等的处理是一个较为复杂和庞大的,分散在各个子系统中将导致这些策略无法统一控制和维护

 

因此,基于上述这些问题,接下来的方案就呼之欲出了 -- 构建统一的认证中心,这样做的好处是显而易见的

 

下面是服务端处理用户请求的流程图:

 

 

方案3 -- 跨域请求

方案2做到了业务系统与 SSO 认证系统的解耦,但是仍然存在一个显著问题,对于浏览器客户端来说一个 domain 的所有子 domain 可以共享 cookie,但是完全不同的域名之间则无法读写 cookie

例如,sso.taobao.com、www.taobao.com 与 xxx.taobao.com 都可以共享 .taobao.com 域下的 cookie,但是所有浏览器都不会将 tmall.com 的 cookie 传递给 sso.taobao.com

这意味着使用上面的方案将导致类似淘宝与天猫的跨域请求不能实现单点登录

于是就有了进一步的可以支持跨域请求的方案:

 

  • 首次登录

下图展示了用户首次登录的系统间交互时序:

 

 

  1. 首次登录的情况下,客户端不带有任何 cookie,服务端以当前 URL 作为 SSO 登录页面的 sourceurl 参数返回 302 跳转连接
  2. 浏览器自动跳转到 SSO 登录页面,SSO 服务发现用户没有带有 cookie,则渲染登录页面
  3. 用户完成登录以后,SSO 验证登录用户名、密码正确,则设置 SSO 的 cookie,同时生成唯一秘钥 token,然后跳转到 sourceurl,并以 token 为参数
  4. 浏览器自动跳转回服务端业务 url,服务端获取到带有 token 参数的请求,使用 token 参数调用 SSO 验证中心接口,验证 token 有效
  5. 服务端执行业务逻辑后返回客户端,同时设置 SSO 验证中心接口生成并返回的 cookie
  6. 此后所有该浏览器对服务端的请求都会自动在 header 中带上 cookie,而不需要业务系统存储或返回 token

 

  • 跨域登录

跨域登录意味着用户不具有业务系统的任何 cookie,但是由于进行过上述首次登录的流程,所以在进行对 SSO 登录中心的所有请求都会带有 SSO 登录中心域名下的 cookie

下图展示了用户跨域登录的系统间交互时序:

 

 

  1. 与上述流程一样,由于客户端不带有任何 cookie,服务端以当前 URL 作为 SSO 登录页面的 sourceurl 参数返回 302 跳转连接
  2. 浏览器自动跳转到 SSO 登录页面,SSO 服务获取到用户的 cookie,进行验证
  3. SSO 服务验证成功,则生成唯一秘钥 token,然后跳转到 sourceurl,并以 token 为参数
  4. 浏览器自动跳转回服务端业务 url,服务端获取到带有 token 参数的请求,使用 token 参数调用 SSO 验证中心接口,验证 token 有效
  5. 服务端执行业务逻辑后返回客户端,同时设置 SSO 验证中心接口生成并返回的 cookie
  6. 此后所有该浏览器对服务端的请求都会自动在 header 中带上 cookie,而不需要业务系统存储或返回 token

 

  • 总结

可以看到,实际上服务端和 SSO 系统分别在上述两种情况下做的事情几乎是完全一致的

在任何情况下,服务端需要做的就是判断请求参数中是否带有 token、header 中是否带有 cookie

如果没有,那么就返回跳转到 SSO 验证接口的 302 跳转,否则调用 SSO 验证中心的接口验证 token 或 cookie

而 SSO 验证中心需要做的仅仅是验证 cookie 及提供 token、业务 cookie 的验证接口

这样的逻辑十分清晰,系统间耦合很低,SSO 验证中心可以通过其验证接口实现对权限、风控进行非常灵活的控制

 

我们看到上述的方案3已经可以解决跨域单点登录的问题,其逻辑也非常清晰,那么,在实际的线上系统中,是否采用了这样的交互方式呢?

我们来通过实例验证一下

 

介绍

ITeye 是 2003 年创办的一个 java 技术社区,此前被 CSDN 低调收购,收购后 https://www.iteye.com/ 仍然被独立运营着,但是 https://www.iteye.com/ 新增了可以使用 csdn 账号登录的入口

这就是典型的跨域单点登录了,因为实际的登录入口是 https://api.csdn.net

那么,我们来抓包看看他是怎么做的

 

客户端请求 -- 服务端的首次跳转

抓包看到,iteye.com 的服务端果然返回了跳转到 http://api.csdn.net 的 302 response,并且带有 redirect_uri=http://www.iteye.com/auth/csdn/callback 的参数,用来通知 SSO 中心即 http://api.csdn.net 验证后的跳转地址,对应我们方案3的步骤1

 

 

307 Internal Redirect

浏览器跳转后,http://api.csdn.net 返回了 Status Code:307 Internal Redirect,并且在 header 中带有 Non-Authoritative-Reason:HSTS

这表示该地址仅接受 https 请求,浏览器收到该提示后,自动跳转到了对应的 https 地址

 

 

SSO 验证中心验证并跳转回 sourceurl

方案3的步骤2,用户登录的过程我们这里不赘述,抓包可以看到,由于我们已经登录过 csdn,所以浏览器自动在 header 中加入了带有秘钥信息的 cookie

https://api.csdn.net 验证秘钥后自动跳转回了 sourceurl,并且带上了 code 参数,这就是我们的方案3步骤3,而 code 参数就是我们上文中的 token

 

 

业务服务验证 token,设置 cookie

浏览器接收到 302 返回后,自动跳转到带有 code 参数的 sourceurl

服务端获取到 code 参数后调用接口验证成功,执行业务逻辑:返回 302 跳转到 https://www.iteye.com,同时设置了一系列 cookie

 

 

至此,我们看到,整个过程与我们上面的方案3完全符合

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,全部原创,只有干货没有鸡汤

 

 






技术帖      技术分享      http      架构      单点登录     


京ICP备15018585号