Spring Boot「28」扩展:SSO 单点登录流程分析

2,144 阅读7分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情

01-SSO 业务流程分析

假设,我们有三个系统,分别是单点登录服务(或者也可称为是统一认证中心、统一认证服务,CAS)、业务系统 A、业务系统 B。

  1. 场景一,用户在统一认证中心未登录条件下,首次访问业务系统 A;
  2. 场景二,用户在统一认证中心已登录条件下,再次访问业务系统 A;
  3. 场景三,用户在统一认证中心已登录条件下,首次访问业务系统 B。

上述三个场景是单点登录模型中的三个典型场景,接下来我将逐个分析,并给出时序图。

01.1-场景一和场景二

场景一、场景二是关联性比较强的场景,所以我放在一起讨论。

img_shiro_sso-login12.png

接下来,我会对图中流程的关键点进行解释,方便大家理解。 步骤 1-3 是用户首次访问业务系统 A 时的处理逻辑: A 会先检查用户是否登录,如果未登录(从技术上讲就是用户请求中没有携带 Session ID,或携带的 Session ID 在当前系统中找不到对应的 Session 对象),会检查请求是否带有认证中心颁发的临时令牌。 由于未在认证中心登录的前提条件,请求中也没有临时令牌,所以系统 A 就认为用户尚未登录,然后会通过重定向的方式告知客户端去 CAS 进行登录认证(步骤 4)。

步骤 5-7 是认证服务的逻辑,判断是否登录。判断方式与前面类似,检查认证中心系统中是否有用户对应的 Session 对象。 这里的 Session 也被称为全局会话(先记住这个名字),指用户与认证中心之间建立的会话。 如果用户未登录,认证中心会提供登录界面让用户登录(7-9)。 登录成功后,认证中心会创建一个全局会话,并且生成一个临时令牌(token),通过重定向响应返回给用户(步骤 11)。

用户拿到统一认证中心的临时令牌后,会再次请求业务系统 A。 A 会重复使用步骤 1-3 去检查用户的请求,发现尚未登录、但是请求中带有临时令牌(13-14)。 然后,它会去认证中心校验令牌的合法性(15-16)。 令牌若合法,系统 A 就知道该用户在认证中心登录过了,就会创建一个 Session(这个也被称为是局部会话),并返回用户请求的资源(17-18)。

注:从上面的流程中可以发现,全局会话是用户与认证系统之间的会话,通过登录这种方式创建的。 局部会话是用户与业务系统之间的,通过校验临时令牌的合法性方式创建的。 当全局会话失效,它生成的临时令牌也将失效,基于临时令牌的局部会话也将全部失效。 这就是全局会话、局部会话之间的依存关系。

前面的步骤都是在用户未在 CAS 登录前提下,首次访问业务系统 A 时的处理流程。 接下来,我们来一起看下局部会话建立后,用户再次访问业务系统 A 时的处理流程。

因为之前已经建立了与业务系统 A 的局部会话,所以再次访问 A,验证是否已登录时,验证结果为已登录。 业务系统就会直接返回用户请求的资源(步骤 20-21)。

01.2-场景三

场景三也是在场景一的基础上进行的。 它的流程图如下所示:

img_shiro_sso-login3.png

用户首次访问业务系统 B 时,与场景一中的步骤 1-3 是相同的。 系统 B 发现用户未登录(不存在用户、B 之间的局部会话),之后检查用户是否带有临时令牌。 这里分为两种情况:

  1. 请求里带有 token
  2. 请求里不带 token

针对第一中情况,系统 B 会去 CAS 验证 token 的合法性,若合法,则会创建系统 B 与用户之间的局部会话,并将用户请求的资源响应给用户(步骤 8-14)。

针对第二种情况,系统 B 会重定向用户请求到 CAS 进行登录。 CAS 收到用户登录请求后,检查发现用户与 CAS 之间已经存在全局会话,则获取创建全局会话时一并生成的临时令牌,并通过重定向响应返回给客户端。 客户端收到 token 后,携带 token 请求系统 B,与处理第一种情况类似。

02-从 HTTP 报文角度分析 SSO 登录流程

在上一节,我们对 SSO 登录的业务流程进行了梳理,大致上明白了 SSO 登录在处理什么问题,处理问题的大致流程是什么。 接下来,我将从 HTTP 报文角度,分析一下每个步骤客户端、业务系统、CAS 认证中心都会接受、发送什么样的报文,进而更深入地理解 SSO 登录过程。 同样地,我会按照上节的三个场景逐个分析。

02.1-场景一和场景二中的 HTTP 报文

步骤一,用户请求业务系统 A 的某个页面:

GET /users HTTP/1.1
Host: businessa.example.samson.self

步骤四,业务系统 A 验证用户发现未登录,重定向到 CAS 去登录:

HTTP/1.1 302 Moved Temporarily
Location: http://cas.example.samson.self/login?backurl=http://businessa.example.samson.self/users

浏览器重定向请求,获得登录界面:

GET /login?backurl=http://businessa.example.samson.self/users HTTP/1.1
Host: cas.example.samson.self

步骤八,用户提交登录请求后,CAS 验证并创建全局会话,再将 token 返回给用户:

POST /login?backurl=http://businessa.example.samson.self/users HTTP/1.1
Host: cas.example.samson.self

步骤十一,用户收到重定向响应:

HTTP/1.1 302 Moved Temporarily
Set-Cookie: JSESSIONID=51d5590d-55c0-48cb-bff2-cdab040f687c
Location: http://businessa.example.samson.self/users?authCode=7d227af1-20e5-49bc-829b-b049aaadd56e&username=lihua

步骤十二,带着令牌访问业务系统 A:

GET /users?authCode=7d227af1-20e5-49bc-829b-b049aaadd56e&username=lihua HTTP/1.1
Host: businessa.example.samson.self

业务系统验证令牌合法性后,返回用户请求的资源(步骤十八):

HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=baf4765d-3d06-4000-aaa1-44fcb9e2b25f

场景二中,用户已经与业务系统 A 建立好局部会话,所以再次请求时,可以直接获得资源:

GET /users HTTP/1.1
Host: businessa.example.samson.self
Cookie: JSESSIONID=baf4765d-3d06-4000-aaa1-44fcb9e2b25f

02.2-场景三中的 HTTP 报文

同样分两种情况,对于第一种情况,即不带令牌访问:

GET /permissions HTTP/1.1
Host: businessb.example.samson.self

业务系统发送重定向响应(带有全局会话 SessionID):

HTTP/1.1 302 Moved Temporarily
Location: http://cas.example.samson.self/login?backurl=http://businessb.example.samson.self/permissions
Cookie: JSESSIONID=51d5590d-55c0-48cb-bff2-cdab040f687c 

CAS 系统返回响应:

HTTP/1.1 302 Moved Temporarily
Location: http://businessb.example.samson.self/permissions?authCode=7d227af1-20e5-49bc-829b-b049aaadd56e&username=lihua

客户端带着令牌访问业务系统 B:

GET /permissions?authCode=7d227af1-20e5-49bc-829b-b049aaadd56e&username=lihua HTTP/1.1
Host: businessb.example.samson.self

业务系统验证令牌合法性,并建立局部会话:

HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=268459a0-3279-4e86-b627-cbe3e3efdf32

针对第 2 种情况,与后面带着令牌访问系统 B 是一样的过程。

03-总结

今天,我介绍了 SSO 登录过程的一般流程,并通过三个典型场景分析了登录流程的处理细节。 并且,在了解大致业务流程后,从 HTTP 报文的角度分析了各个模块(客户端、业务系统、统一认证服务)在每个流程中的一般动作。 我只在分析过程中给出了 HTTP 报文的头部信息。 感兴趣的小伙伴可以自己动手实现下上述过程,并观察下整个流程中发送的 HTTP 报文情况,是否跟我介绍的流程一致。 希望今天的内容能够对你有所帮助。