# 单点登录解决方案

## 共享 Session

```plantuml
@startuml

actor User order 1
participant Browser order 2
participant "SSO Server" order 3
participant App1 order 4
participant App2 order 5
database Redis order 6

== 第一次访问 App1 ==

User -> Browser: 访问 App1
activate Browser

Browser -> App1: GET https://app1.example.com/
activate App1

return 302 Location: https://sso.example.com/login?\nservice=https%3A%2F%2Fapp1.example.com%2F

activate Browser
Browser -> "SSO Server": GET https://sso.example.com/login?\nservice=https%3A%2F%2Fapp1.example.com%2F
activate "SSO Server"

return SSO 登录表单

Browser -> User: 展示 SSO 登录表单
activate User

return: 提交 SSO 登录表单

Browser -> "SSO Server": POST https://sso.example.com/login?\nservice=https%3A%2F%2Fapp1.example.com%2F
activate "SSO Server"

"SSO Server" -> "SSO Server": 认证用户

"SSO Server" -> Redis: set sso:ABC1234567 {"username": "Tom"}
activate Redis

return ok

return Set-Cookie: JSESSIONID=ABC1234567; Domain=example.com\n302 Location: https://app1.example.com/
deactivate Browser

Browser -> App1: Cookie: JSESSIONID=ABC1234567\nGET https://app1.example.com/
activate App1

App1 -> Redis: get sso:ABC1234567
activate Redis

return {"username": "Tom"}

return 200 [https://app1.example.com/]

return 展示 App1

== 第二次访问 App1 ==

User -> Browser: 访问 App1
activate Browser

Browser -> App1: Cookie: JSESSIONID=ABC1234567\nGET https://app1.example.com/resource/
activate App1

App1 -> Redis: get sso:ABC1234567
activate Redis

return {"username": "Tom"}

return 200 [https://app1.example.com/resource/]

return 展示 App1

== 第一次访问 App2 ==

User -> Browser: 访问 App2
activate Browser

Browser -> App2: Cookie: JSESSIONID=ABC1234567\nGET https://app2.example.com/
activate App2

App2 -> Redis: get sso:ABC1234567
activate Redis

return {"username": "Tom"}

return 200 [https://app2.example.com/]

return 展示 App2

@enduml
```

优点：

1. 实现单点登出比较简单

缺点：

1. 不支持跨顶级域名登录
2. 严重依赖外部系统（Redis）

## JSON Web Token (JWT)

```plantuml
@startuml

actor User order 1
participant Browser order 2
participant "SSO Server" order 3
participant App1 order 4
participant App2 order 5

== 第一次访问 App1 ==

User -> Browser: 访问 App1
activate Browser

Browser -> App1: GET https://app1.example.com/
activate App1

return 302 Location: https://sso.example.com/login?\nservice=https%3A%2F%2Fapp1.example.com%2F

activate Browser
Browser -> "SSO Server": GET https://sso.example.com/login?\nservice=https%3A%2F%2Fapp1.example.com%2F
activate "SSO Server"

return SSO 登录表单

Browser -> User: 展示 SSO 登录表单
activate User

return: 提交 SSO 登录表单

Browser -> "SSO Server": POST https://sso.example.com/login?\nservice=https%3A%2F%2Fapp1.example.com%2F
activate "SSO Server"

"SSO Server" -> "SSO Server": 认证用户

"SSO Server" -> "SSO Server": 生成 JWT\nheader + payload + secret -> abc.def.ghi

return Set-Cookie: abc.def.ghi; Domain=example.com\n302 Location: https://app1.example.com/
deactivate Browser

Browser -> App1: Authorization: Bearer abc.def.ghi\nGET https://app1.example.com/
activate App1

App1 -> App1: 解析 JWT\nabc.def.ghi -> header + payload + secret

return 200 [https://app1.example.com/]

return 展示 App1

== 第二次访问 App1 ==

User -> Browser: 访问 App1
activate Browser

Browser -> App1: Authorization: Bearer abc.def.ghi\nGET https://app1.example.com/resource/
activate App1

App1 -> App1: 解析 JWT\nabc.def.ghi -> header + payload + secret

return 200 [https://app1.example.com/resource/]

return 展示 App1

== 第一次访问 App2 ==

User -> Browser: 访问 App2
activate Browser

Browser -> App2: Authorization: Bearer abc.def.ghi\nGET https://app2.example.com/
activate App2

App2 -> App2: 解析 JWT\nabc.def.ghi -> header + payload + secret

return 200 [https://app2.example.com/]

return 展示 App2

@enduml
```

优点：

1. 服务端无状态

缺点：

1. 不支持跨顶级域名登录
2. 实现单点登出（失效 Token）比较复杂
3. 续签 Token 比较复杂
4. 需要额外的计算成本

## Central Authentication Service (CAS)

```plantuml
@startuml

actor User order 1
participant Browser order 2
participant "CAS Server" order 3
participant App1 order 4
participant App2 order 5

== 第一次访问 App1 ==

User -> Browser: 访问 App1
activate Browser

Browser -> App1: GET https://app1.example.com/
activate App1

return 302 Location: https://cas.example.com/cas/login?\nservice=https%3A%2F%2Fapp1.example.com%2F

activate Browser
Browser -> "CAS Server": GET https://cas.example.com/cas/login?\nservice=https%3A%2F%2Fapp1.example.com%2F
activate "CAS Server"

return CAS 登录表单

Browser -> User: 展示 CAS 登录表单
activate User

return: 提交 CAS 登录表单

Browser -> "CAS Server": POST https://cas.example.com/cas/login?\nservice=https%3A%2F%2Fapp1.example.com%2F
activate "CAS Server"

"CAS Server" -> "CAS Server": 认证用户

return Set-Cookie: CASTGC=TGT-2345678\n302 Location: https://app1.example.com/?\nticket=ST-12345678
deactivate Browser

Browser -> App1: GET https://app1.example.com/?\nticket=ST-12345678
activate App1

App1 -> "CAS Server": GET https://cas.example.com/serviceValidate?\nservice=https%3A%2F%2Fapp1.example.com%2F%3F\nticket%3DST-12345678
activate "CAS Server"

return 200 [XML]

return Set-Cookie: JSESSIONID=ABC1234567\n 302 Location: https://app1.example.com/

Browser -> App1: Cookie: JSESSIONID=ABC1234567\nGET https://app1.example.com/
activate App1

App1 -> App1: 校验 session 和 cookie

return 200 [https://app1.example.com/]

return 展示 App1

== 第二次访问 App1 ==

User -> Browser: 访问 App1
activate Browser

Browser -> App1: Cookie: JSESSIONID=ABC1234567\nGET https://app1.example.com/resource/
activate App1

App1 -> App1: 校验 session 和 cookie

return 200 [https://app1.example.com/resource/]

return 展示 App1

== 第一次访问 App2 ==

User -> Browser: 访问 App2
activate Browser

Browser -> App2: GET https://app2.example.com/
activate App2

return 302 Location: https://cas.example.com/cas/login?\nservice=https%3A%2F%2Fapp2.example.com%2F

activate Browser
Browser -> "CAS Server": Cookie: CASTGC=TGT-2345678\nGet https://cas.example.com/cas/login?\nservice=https%3A%2F%2Fapp2.example.com%2F
activate "CAS Server"

"CAS Server" -> "CAS Server": 校验 TGT

return 302 Location: https://app2.example.com/?\nticket=ST-345678
deactivate Browser

Browser -> App2: GET https://app2.example.com/?ticket=ST-345678
activate App2

App2 -> "CAS Server": GET https://cas.example.com/serviceValidate?\nservice=https%3A%2F%2Fapp2.example.com%2F%3F\nticket%3DST-345678
activate "CAS Server"

return 200 [XML]

return Set-Cookie: JSESSIONID=DEF1234567\n 302 Location: https://app2.example.com/

Browser -> App2: Cookie JSESSIONID=DEF1234567\n https://app2.example.com/
activate App2

App2 -> App2: 校验 session 和 cookie

return 200 [https://app2.example.com/]

return 展示 App2

@enduml
```

优点：

1. 支持跨顶级域名登录

缺点

1. 实现单点登出比较复杂


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.fantasticmao.cn/tech/fen-bu-shi-xi-tong/dan-dian-deng-lu-jie-jue-fang-an.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
