【后端】多个后端系统,如何共用一套登录状态?单点登录详解

简单来说,要实现"一次登录,两个后端都能访问权限",最成熟和通用的方案是采用 单点登录(SSO) 架构。

🧠 核心概念:单点登录(SSO)

单点登录(SSO) 是一种身份认证机制,允许用户在多个相互信任的应用系统中,只需登录一次,即可访问所有授权的应用,而无需重复输入凭证。其核心在于有一个独立的 认证中心(Identity Provider, IdP) 来统一管理用户的认证过程,而各个业务应用(Service Provider, SP)则将认证请求委托给这个中心处理。

🔄 主流SSO实现方案对比

目前业界实现SSO的方案主要有三种,你可以根据你的具体场景(如同域/跨域、前后端分离、微服务架构、安全性要求等)来选择。

特性维度 基于Cookie/Session共享 基于Token(如JWT) 基于OAuth2.0/OIDC
核心机制 服务器端Session + 客户端Cookie 无状态令牌(JWT) 标准授权协议(OAuth2.0) + 身份层(OIDC)
适用场景 同域 下的传统Web应用(如 app1.example.com, app2.example.com 前后端分离微服务跨域场景,需要无状态服务 需要第三方授权企业级复杂SSO跨域跨组织标准协议对接
优点 实现相对简单,服务器可控性强 无状态,服务器压力大,易于横向扩展;跨域友好 标准化协议,生态成熟,安全性高(如授权码模式);支持细粒度权限控制;支持第三方登录
缺点 难以解决跨域问题;Session共享需要额外配置;服务器有状态 Token一旦签发难以主动撤销;Token尺寸可能大于SessionId 协议和实现相对复杂;需要额外的认证服务器
技术实现参考 Spring Session + Redis Spring Security + JWT Spring Authorization Server / Keycloak

🔍 方案详解:如何做到一次登录,双端访问

为了让你更清晰地理解,我们通过一个通用的SSO流程来解析,并结合OAuth2.0授权码模式(最安全、最常用的模式)来举例。

1. SSO的通用核心流程

下图描述了用户首次访问应用A、然后无需再次登录即可访问应用B的核心交互过程:
#mermaid-svg-D8iWFlUgdWEATeyx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-D8iWFlUgdWEATeyx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-D8iWFlUgdWEATeyx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-D8iWFlUgdWEATeyx .error-icon{fill:#552222;}#mermaid-svg-D8iWFlUgdWEATeyx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-D8iWFlUgdWEATeyx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-D8iWFlUgdWEATeyx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-D8iWFlUgdWEATeyx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-D8iWFlUgdWEATeyx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-D8iWFlUgdWEATeyx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-D8iWFlUgdWEATeyx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-D8iWFlUgdWEATeyx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-D8iWFlUgdWEATeyx .marker.cross{stroke:#333333;}#mermaid-svg-D8iWFlUgdWEATeyx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-D8iWFlUgdWEATeyx p{margin:0;}#mermaid-svg-D8iWFlUgdWEATeyx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-D8iWFlUgdWEATeyx .cluster-label text{fill:#333;}#mermaid-svg-D8iWFlUgdWEATeyx .cluster-label span{color:#333;}#mermaid-svg-D8iWFlUgdWEATeyx .cluster-label span p{background-color:transparent;}#mermaid-svg-D8iWFlUgdWEATeyx .label text,#mermaid-svg-D8iWFlUgdWEATeyx span{fill:#333;color:#333;}#mermaid-svg-D8iWFlUgdWEATeyx .node rect,#mermaid-svg-D8iWFlUgdWEATeyx .node circle,#mermaid-svg-D8iWFlUgdWEATeyx .node ellipse,#mermaid-svg-D8iWFlUgdWEATeyx .node polygon,#mermaid-svg-D8iWFlUgdWEATeyx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-D8iWFlUgdWEATeyx .rough-node .label text,#mermaid-svg-D8iWFlUgdWEATeyx .node .label text,#mermaid-svg-D8iWFlUgdWEATeyx .image-shape .label,#mermaid-svg-D8iWFlUgdWEATeyx .icon-shape .label{text-anchor:middle;}#mermaid-svg-D8iWFlUgdWEATeyx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-D8iWFlUgdWEATeyx .rough-node .label,#mermaid-svg-D8iWFlUgdWEATeyx .node .label,#mermaid-svg-D8iWFlUgdWEATeyx .image-shape .label,#mermaid-svg-D8iWFlUgdWEATeyx .icon-shape .label{text-align:center;}#mermaid-svg-D8iWFlUgdWEATeyx .node.clickable{cursor:pointer;}#mermaid-svg-D8iWFlUgdWEATeyx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-D8iWFlUgdWEATeyx .arrowheadPath{fill:#333333;}#mermaid-svg-D8iWFlUgdWEATeyx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-D8iWFlUgdWEATeyx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-D8iWFlUgdWEATeyx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-D8iWFlUgdWEATeyx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-D8iWFlUgdWEATeyx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-D8iWFlUgdWEATeyx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-D8iWFlUgdWEATeyx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-D8iWFlUgdWEATeyx .cluster text{fill:#333;}#mermaid-svg-D8iWFlUgdWEATeyx .cluster span{color:#333;}#mermaid-svg-D8iWFlUgdWEATeyx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-D8iWFlUgdWEATeyx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-D8iWFlUgdWEATeyx rect.text{fill:none;stroke-width:0;}#mermaid-svg-D8iWFlUgdWEATeyx .icon-shape,#mermaid-svg-D8iWFlUgdWEATeyx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-D8iWFlUgdWEATeyx .icon-shape p,#mermaid-svg-D8iWFlUgdWEATeyx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-D8iWFlUgdWEATeyx .icon-shape .label rect,#mermaid-svg-D8iWFlUgdWEATeyx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-D8iWFlUgdWEATeyx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-D8iWFlUgdWEATeyx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-D8iWFlUgdWEATeyx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 业务应用B
业务应用A
认证中心 IdP
用户浏览器

  1. 访问App1
  2. 重定向到SSO登录页
  3. 用户输入账号密码
  4. 提交凭证
  5. 验证成功,生成Token

(如JWT)并返回
6. 携带Token访问App1后端
7. 携带Token验证
8. 验证Token有效性,返回用户信息
9. 建立会话,允许访问
10. 访问App2
11. 重定向到SSO认证
12. 检测到已有有效Token

(通过Cookie或重定向参数)
13. 重定向回App2,附带Token
14. 携带Token访问App2后端
15. 携带Token验证
16. 验证Token有效性,返回用户信息
17. 建立会话,允许访问
用户
认证服务器

(登录、签发Token)
验证服务

(校验Token有效性)
前端页面
后端服务
前端页面
后端服务

📖 流程步骤详解

  1. 用户访问应用A:用户尝试访问业务应用A的受保护资源。
  2. 重定向到认证中心:应用A检测到用户未登录,将用户重定向到认证中心(IdP)的统一登录页面。
  3. 用户认证:用户在认证中心输入用户名和密码进行登录。
  4. 认证中心生成Token :认证中心验证用户凭证成功后,生成一个认证令牌(Token)(例如,一个签名的JWT),这个Token包含了用户的身份信息。
  5. Token返回与应用A建立会话 :认证中心将Token(或一个临时的授权码)返回给用户浏览器,并重定向回应用A指定的回调地址。应用A的后端获取到这个Token(或通过授权码换取Token),验证其有效性(如校验签名、有效期),然后在自己的系统中为用户创建一个本地会话,允许用户访问。
  6. 用户访问应用B:用户随后访问应用B的受保护资源。
  7. 应用B重定向到认证中心:应用B同样检测到用户未登录(本地没有会话),将用户重定向到认证中心。
  8. 认证中心检测已登录状态 :认证中心检测到用户浏览器中已存在有效的认证凭证(如包含认证信息的Cookie,或通过某种方式传回的Token),识别用户已处于登录状态
  9. 认证中心生成新Token并重定向回应用B:认证中心为应用B生成一个新的Token(或授权码),并将用户重定向回应用B的回调地址,附带上这个Token。
  10. 应用B验证Token并建立会话 :应用B的后端获取到Token,向认证中心验证其有效性。验证通过后,应用B在自己的系统中为用户创建本地会话,允许用户访问,无需用户再次输入密码。
2. 关键技术点:如何实现"共享"

无论选择哪种方案,实现"一次登录,双端访问"的核心在于如何让两个后端应用信任并验证同一个认证凭证

  • 基于Token(如JWT)的方案 :这是目前最推荐和流行的方案。
    • 无状态与自包含 :JWT(JSON Web Token)是一个紧凑的、自包含的令牌。它将用户信息(如用户ID、角色)编码在令牌本身,并使用一个密钥进行签名。
    • 信任转移两个后端应用共享同一个用于验证JWT签名的密钥 。当应用A签发一个JWT后,应用B收到这个JWT,只需使用这个共享密钥验证签名是否有效、是否过期,就可以确认用户的身份,无需再查询认证中心。这真正实现了"一次认证,处处访问"。
    • Token传递 :用户在请求应用B时,通常将JWT放在HTTP请求的Authorization header中(如 Bearer <token>)。
  • 基于OAuth2.0/OIDC的方案 :这是更企业级、标准化的方案。
    • 授权码模式 :这是OAuth2.0中最安全、最完整的授权模式,非常适合用于实现SSO。
      • 应用A :用户在应用A登录时,应用A会将用户重定向到认证服务器,获取一个授权码(Authorization Code) 。然后,应用A的后端使用这个授权码、自己的client_idclient_secret向认证服务器换取一个访问令牌(Access Token)。这个访问令牌可以用来调用认证服务器的用户信息接口(UserInfo Endpoint)获取用户详情。
      • 应用B:当用户访问应用B时,流程类似。认证服务器检测到用户已登录(通过浏览器中的Cookie或其他机制),会直接颁发一个新的授权码给应用B。应用B的后端同样使用授权码换取访问令牌,并获取用户信息。
    • OIDC的增强 :OIDC在OAuth2.0上增加了一层身份层,直接提供了一个ID Token(JWT),其中包含了用户身份信息。应用在拿到ID Token后,除了验证签名,还可以直接从中解析用户信息,减少了调用用户信息接口的次数。
3. 不同方案下的具体实现路径

你可以参考下面的流程图来选择和规划你的实现路径:
#mermaid-svg-Tw0LzIZolFhfQuug{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Tw0LzIZolFhfQuug .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Tw0LzIZolFhfQuug .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Tw0LzIZolFhfQuug .error-icon{fill:#552222;}#mermaid-svg-Tw0LzIZolFhfQuug .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Tw0LzIZolFhfQuug .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Tw0LzIZolFhfQuug .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Tw0LzIZolFhfQuug .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Tw0LzIZolFhfQuug .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Tw0LzIZolFhfQuug .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Tw0LzIZolFhfQuug .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Tw0LzIZolFhfQuug .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Tw0LzIZolFhfQuug .marker.cross{stroke:#333333;}#mermaid-svg-Tw0LzIZolFhfQuug svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Tw0LzIZolFhfQuug p{margin:0;}#mermaid-svg-Tw0LzIZolFhfQuug .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Tw0LzIZolFhfQuug .cluster-label text{fill:#333;}#mermaid-svg-Tw0LzIZolFhfQuug .cluster-label span{color:#333;}#mermaid-svg-Tw0LzIZolFhfQuug .cluster-label span p{background-color:transparent;}#mermaid-svg-Tw0LzIZolFhfQuug .label text,#mermaid-svg-Tw0LzIZolFhfQuug span{fill:#333;color:#333;}#mermaid-svg-Tw0LzIZolFhfQuug .node rect,#mermaid-svg-Tw0LzIZolFhfQuug .node circle,#mermaid-svg-Tw0LzIZolFhfQuug .node ellipse,#mermaid-svg-Tw0LzIZolFhfQuug .node polygon,#mermaid-svg-Tw0LzIZolFhfQuug .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Tw0LzIZolFhfQuug .rough-node .label text,#mermaid-svg-Tw0LzIZolFhfQuug .node .label text,#mermaid-svg-Tw0LzIZolFhfQuug .image-shape .label,#mermaid-svg-Tw0LzIZolFhfQuug .icon-shape .label{text-anchor:middle;}#mermaid-svg-Tw0LzIZolFhfQuug .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Tw0LzIZolFhfQuug .rough-node .label,#mermaid-svg-Tw0LzIZolFhfQuug .node .label,#mermaid-svg-Tw0LzIZolFhfQuug .image-shape .label,#mermaid-svg-Tw0LzIZolFhfQuug .icon-shape .label{text-align:center;}#mermaid-svg-Tw0LzIZolFhfQuug .node.clickable{cursor:pointer;}#mermaid-svg-Tw0LzIZolFhfQuug .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Tw0LzIZolFhfQuug .arrowheadPath{fill:#333333;}#mermaid-svg-Tw0LzIZolFhfQuug .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Tw0LzIZolFhfQuug .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Tw0LzIZolFhfQuug .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tw0LzIZolFhfQuug .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Tw0LzIZolFhfQuug .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tw0LzIZolFhfQuug .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Tw0LzIZolFhfQuug .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Tw0LzIZolFhfQuug .cluster text{fill:#333;}#mermaid-svg-Tw0LzIZolFhfQuug .cluster span{color:#333;}#mermaid-svg-Tw0LzIZolFhfQuug div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Tw0LzIZolFhfQuug .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Tw0LzIZolFhfQuug rect.text{fill:none;stroke-width:0;}#mermaid-svg-Tw0LzIZolFhfQuug .icon-shape,#mermaid-svg-Tw0LzIZolFhfQuug .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Tw0LzIZolFhfQuug .icon-shape p,#mermaid-svg-Tw0LzIZolFhfQuug .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Tw0LzIZolFhfQuug .icon-shape .label rect,#mermaid-svg-Tw0LzIZolFhfQuug .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Tw0LzIZolFhfQuug .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Tw0LzIZolFhfQuug .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Tw0LzIZolFhfQuug :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 同域名下

(如 app1.yoursite.com, app2.yoursite.com
前后端分离/微服务/跨域
需要标准化协议/企业级SSO/第三方登录
需求: 一次登录, 双端访问权限
应用场景判断
方案一: Cookie/Session共享
设置Cookie的Domain为父域

(如 .yoursite.com)
使用Spring Session等框架

将Session存储在Redis等共享存储中
两个应用共享同一Session数据
方案二: 基于Token(JWT)
认证中心签发JWT

(包含用户信息、签名)
两个后端应用共享

JWT签名的密钥
应用B在收到请求后

自行验证JWT的签名和有效期
方案三: OAuth2.0/OIDC
部署认证服务器

(如Keycloak、Spring Authorization Server)
两个应用注册为客户端

(client_id, client_secret)
应用A通过授权码换取Token

应用B通过授权码换取Token
两个应用通过Token

向认证服务器验证或获取用户信息


🛠️ 实施步骤与选择建议

1. 前置条件与规划
  • 统一用户目录 :两个后端系统必须使用同一个用户源(如同一个数据库表、LDAP或AD)。这是SSO的基础,否则即使认证通过,也无法知道"你是系统A的用户1"就是"系统B的用户1"。
  • 选择认证中心(IdP)
    • 小型项目/快速验证:可以自己基于JWT实现一个简单的认证服务。
    • 中型项目/企业应用 :强烈建议使用开源的认证服务器 ,如 KeycloakSpring Authorization Server(基于Spring Security 5.7+的新认证服务器框架)。它们开箱即支持OAuth2.0、OIDC、SSO、用户管理等功能。
  • 选择协议 :对于大多数现代Web应用和API,OAuth2.0 + OIDC 是最推荐的选择,它兼顾了安全性、标准化和灵活性。
2. 核心开发任务(以OAuth2.0/OIDC为例)
角色 主要任务 关键点
认证中心 (IdP) 1. 实现用户登录界面和凭证校验。 2. 管理客户端注册信息(client_id, client_secret, redirect_uri)。 3. 实现授权码和令牌的颁发、校验、刷新。 4. 提供用户信息接口(UserInfo Endpoint)。 安全性 是核心。确保使用HTTPS、授权码短期有效且只能使用一次、client_secret安全存储、严格校验redirect_uri
业务应用A & B 1. 实现受保护资源的拦截器/过滤器。 2. 检测到未登录时,重定向到认证中心的授权地址。 3. 处理认证中心的回调,获取授权码。 4. 后端使用授权码换取访问令牌(Access Token)和ID Token(JWT)。 5. 验证ID Token的签名和有效性,提取用户信息,创建本地会话或进行鉴权。 不要在前端处理敏感信息 (如client_secret)。所有令牌交换和验证都在后端完成。
3. 选择决策矩阵

你可以根据以下情况快速决策:

你的场景是... 最推荐的方案是...
两个应用在同一个顶级域名下 (如 app1.company.com, app2.company.com 方案一:Cookie/Session共享(配置简单,但非跨域的万全之策)
前后端分离的API,或微服务架构 方案二:基于Token(JWT)(无状态,易于扩展,是微服务的标配)
需要企业级SSO、需与第三方系统对接、或需要复杂权限管理 方案三:OAuth2.0/OIDC(标准化,安全,生态强大,是长期演进的最佳选择)

💡 小贴士JWT 方案本身可以看作是 OAuth2.0 协议中令牌的一种具体形式。在很多场景下,它们可以结合使用,比如在OAuth2.0的授权码模式流程中,最终颁发的访问令牌(Access Token)ID Token 就可以是JWT。


⚠️ 重要注意事项

  1. 安全性是生命线
    • 强制HTTPS:所有认证相关的通信必须通过HTTPS进行,防止令牌在传输过程中被窃取。
    • 密钥管理 :JWT的签名密钥或client_secret必须安全存储,绝不能泄露。定期轮换密钥。
    • 令牌有效期:为令牌设置合理的过期时间,不要设置永久有效的令牌。
    • 防重放攻击:授权码是一次性的,使用后应立即失效。
  2. 跨域问题
    • 如果前后端分离,前端和应用后端不在同一个域,需要处理CORS(跨源资源共享) 问题。确保后端正确配置了响应头,允许来自认证中心的请求和携带认证凭证(如Cookie)。
  3. 单点登出
    • SSO不仅包含"一次登录",还应考虑"一次登出"。当用户在认证中心登出时,应通知所有已登录的应用系统清除本地会话。这通常需要应用系统定期向认证中心轮询会话状态,或由认证中心发布登出事件。
  4. 性能考量
    • 基于JWT的方案,后端每次请求都需要验证签名,计算成本相对固定,但无状态设计使得服务器压力更小。
    • 基于OAuth2.0的方案,如果频繁调用认证服务器的接口(如验证Token),可能会产生额外的网络开销。可以采用本地缓存已验证的Token信息来优化性能。

✅ 总结

要实现"两个后端登录一次,都能访问权限",本质是构建一个统一的身份认证体系(SSO)

  • 如果你的系统在同域 且追求快速实现,可以从 Cookie/Session共享 开始。
  • 如果你的系统是前后端分离或微服务架构基于JWT的无状态认证是现代应用的首选。
  • 如果你需要企业级、标准化的解决方案 ,或者未来有对接第三方系统、实现复杂授权的需求,OAuth2.0/OIDC 是最稳健、最长远的选择。部署一个开源认证服务器(如Keycloak)能让你快速获得强大的功能。
    希望这份详细的解释能帮助你理清思路,选择最适合自己的技术路线。如果你对某个方案有更具体的实现问题,我很乐意进一步探讨。
相关推荐
专注写bug9 小时前
Java线程池——ThreadLocal上下文污染问题
java
武子康9 小时前
Java-09 深入浅出 MyBatis 注解开发详解:从 CRUD 到复杂关系映射
java·后端·spring
用户2986985301410 小时前
Java 进阶:在 Word 文档中动态增删页面
java·后端
likerhood10 小时前
Java 集合框架入门:List、Set、Queue 与 Map
java·开发语言·list
Java 码思客10 小时前
【Spring AI实战】第2章 大模型基础调用:同步/异步/流式输出
java·人工智能·spring·ai
郝学胜-神的一滴10 小时前
系统设计 013:高并发系统缓存:从原理到实践全解析
java·开发语言·python·缓存·系统架构·php·软件构建
欧米欧10 小时前
C++进阶之AVL树
java·服务器·c++
我是一只码蚁10 小时前
记一次苍穹外卖项目 Maven 编译报错的排查与解决全过程
java·经验分享·笔记·后端·架构·maven
i220818 Faiz Ul10 小时前
理财系统|基于java+vue的家庭理财系统小程序(源码+数据库+文档)
java·vue.js·spring boot·小程序·论文·毕设·理财系统