☰
ruoyi-vue-pro二开实践:新增yudao-module-sso单点登录模块
2026/9/26 17:46:08 网站建设 项目流程

如果你和我一样,手里长期维护着一套基于ruoyi-vue-pro二开的中后台平台,大概率早晚会被一个问题找上门:企业内部系统越来越多,报表、工单、审批、监控各自一套账号密码,用户在系统间来回跳转,登录登出恨不得占半天工。我这边的客户提了一整年的需求,最后实在绕不过去,决定在项目里新增一个通用的单点登录模块yudao-module-sso,专门解决多系统统一认证和登录态打通的问题。这篇文章就把整个设计和落地过程梳理一遍,包括模块边界、数据模型、三种内嵌单点登录实现方式、实际部署的配置和代码,以及我在对接过程中踩过的坑。如果你正在用 ruoyi-vue-pro 做二开,想把 SSO 能力补上,这篇文章可以直接当落地参考。

1. 先想清楚边界:为什么认证体系里还需要再拆一个 SSO 模块

1.1 已有能力盘点:登录、令牌、OAuth2 都覆盖了,缺的是什么

ruoyi-vue-pro 本身不是一个"没有认证"的框架。它自带账号密码登录、短信登录、微信/钉钉等第三方登录,还提供了 Spring Security 和 Sa-Token 两套会话机制(不同版本有差异),token 签发、刷新、拦截器都是现成的。模块层面也有yudao-module-system管用户、角色、权限,yudao-module-oauth2实现了 OAuth2 授权服务器能力。

那为什么还要新增yudao-module-sso?这是我在动手前反复确认的问题。如果只是"用户登录后拿到 token 去访问多个子系统",那依赖 OAuth2 或者直接共享 token 就够了。但实际情况比这复杂:

  • 单点登录中的"主系统"往往需要给"子应用"提供跳转登录入口,不能每次都用账号密码,而是子应用带着来源标识跳转过来,主站登录后带着凭证回去。
  • 子应用不一定是 ruoyi-vue-pro 体系的,可能是老系统、第三方部署的系统,甚至只有简单后端接口。它不能直接信任主站的 token,需要一个机制完成"主站登录态 -> 子站本地登录态"的转换。
  • 企业场景下,SSO 不只服务 Web 端,还要考虑回调地址白名单、登出广播、登录审计,这些逻辑如果塞进 system 模块,会让用户管理模块变得臃肿。

所以我最终确定的定位是:yudao-module-sso 是一个独立于用户管理和 OAuth2 的接入层模块,目标是让任意子系统都能通过标准的跳转+票据方式接入统一登录,同时保留 OAuth2 这类开放授权能力给外部开发者使用。

1.2 模块边界设计:yudao-module-sso 与其他模块的分工

模块之间的职责边界,我建议按下面的方式划分,这也是我在项目里注册模块依赖时遵循的原则:

模块核心职责和 SSO 模块的关系
yudao-module-system用户管理、角色权限、多租户、操作日志SSO 模块通过 API 读取用户信息、创建会话,不直接操作用户表
yudao-module-oauth2开放授权、统一 token 管理、OAuth2 授权码OAuth2 可以作为 SSO 的一种接入方式,但 SSO 不依赖它作为唯一实现
yudao-module-infra配置管理、文件、定时任务、日志SSO 登录审计、异步任务记录复用它
yudao-module-sso应用注册、票据签发与校验、登出广播、接入方 SDK新增模块,不侵入既有业务

为什么说"不能直接扩展 oauth2 就行"?核心原因是方向不同。OAuth2 是"资源授权"模型,强调的是第三方应用在用户授权后获取访问资源的能力;而企业内部门户场景的 SSO,往往只有一个中心登录页和一个主登录态,子应用只是"借"这个登录态完成自己的本地会话建立,并不需要复杂的 scope 授权流程。如果硬用 OAuth2 模型套 SSO,会出现一个很尴尬的结果:每个子系统都要维护自己的 client 配置、回调地址、scope 定义,接入成本被人为抬高。

1.3 项目落地的实际收益

这个模块建好之后,最直接的收益不是"多了一个功能",而是把之前零散的接入方式收敛了。我的项目里同时存在老 PHP 系统、Spring Boot 2 的旧服务、以及新起的 ruoyi 体系服务,以前每个系统都需要单独开发登录页,处理密码同步,现在统一走 SSO 跳转,用户在任何一个子系统里点登录,都会跳回统一登录页;登录完成后自动回到原来的页面并建立会话。

从长期维护角度看,修改密码策略、开启双因子认证、封禁账号这类操作,也只需要在主站做一遍,所有接进来的系统自动生效。这是我在设计阶段感受最深的价值点:减少重复建设只是显性收益,收敛认证入口带来的管理便利才是更值钱的部分。

2. 通用 SSO 的数据模型与核心流程

2.1 应用注册表:appId、appSecret 与回调白名单

既然要给多个子系统提供接入能力,第一步就是做好"应用注册"的数据模型。这里借鉴了 OAuth2 的 client 模型,但不完全照搬,核心表我设计了三张:应用表、票据表、登录审计表。

应用表yudao_sso_app的字段设计如下:

CREATE TABLE `yudao_sso_app` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `app_name` varchar(64) NOT NULL COMMENT '应用名称,如报表系统', `app_id` varchar(32) NOT NULL COMMENT '应用标识,全局唯一', `app_secret` varchar(64) NOT NULL COMMENT '应用密钥,用于签名回调请求', `redirect_uris` varchar(512) NOT NULL COMMENT '回调地址白名单,多个用逗号分隔', `logout_uri` varchar(512) DEFAULT NULL COMMENT '登出通知地址', `sso_mode` tinyint NOT NULL DEFAULT '1' COMMENT '接入模式:1-票据重定向 2-Cookie共享 3-OAuth2', `status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0-启用 1-禁用', `expire_duration` int NOT NULL DEFAULT '300' COMMENT '票据有效期,单位秒', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime NOT NULL COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '逻辑删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_app_id` (`app_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='单点登录应用表';

几个设计细节我重点说一下。

回调地址白名单之所以要用逗号分隔而不是单独建子表,是因为实际接入的应用,回调地址基本只有一到两个,拿子表存反而增加代码复杂度和跨表查询。等到真出现一个应用有几十个回调域名的情况,再升级为子表也来得及,这个度我自己认为比较合适。

票据表yudao_sso_ticket不一定要建成物理表,我实际用的是 Redis 来存储,表结构只是预留审计和故障排查用。核心字段就是 ticketId、appId、用户ID、状态、过期时间。如果用 Redis,key 建议设计成sso:ticket:{ticketId},value 存 JSON 字符串,包含 uid、appId、创建时间,TTL 和应用的expire_duration保持一致。

2.2 票据签发与校验的完整时序

信息在应用中流转的时序,是整个 SSO 模块的心脏。我先把默认的票据重定向模式画成文字流程,因为后面所有代码都是围绕它展开的:

  1. 用户访问子系统(比如报表系统),未登录,子系统后端给前端一个跳转 URL,格式为:https://sso.example.com/sso/login?appId=report&redirectUri=https://report.example.com/callback。
  2. 用户在统一登录页登录,主站认证框架(我是用 Sa-Token)创建主站会话。
  3. 登录成功后,由yudao-module-sso的服务端生成一次性票据ticketId(UUID),存 Redis,同时把用户重定向回redirectUri,并拼接参数:https://report.example.com/callback?ticket=xxx&appId=report&timestamp=123。
  4. 子系统收到回调请求,拿着 ticket 参数,调用 SSO 模块提供的校验接口:POST /sso/validate,参数为appId、ticket、timestamp,请求头里带appSecret签名。
  5. SSO 模块校验签名、校验票据存在、校验票据说对应用 ID,如果都通过,删除这个 ticket(防止重放),返回用户基本信息(uid、username、nickname)和主站 token。
  6. 子系统拿到用户信息后,在自己的体系里查用户或自动建账号,建立本地会话,然后用户回到正常页面。

这里最关键的设计是"一次性票据"。它和很多初学者的做法不同,不是把主站 token 直接交给子系统,而是用票据换取用户信息,然后票据立刻失效。你可以把它类比成演唱会门票:进场的时候检票员撕掉票根,你才能进入场馆,撕掉的票根不可能再用第二次。这样即使回调地址被监听或者票据在传输过程中泄露,攻击者在票据失效之后也无法再冒用。

2.3 会话同步:登出时如何通知所有已接入应用

单点登录有一句俗话:登录靠跳转,登出靠广播。如果用户在主站点了退出,子系统的会话还活着,下次访问仍然不需要登录,这个"单点登录"就是残缺的。

实现登出广播,我采用的是最务实的方案:主站登出后,遍历所有启用的应用,异步调用各应用注册的logout_uri,并在请求参数里附上userId、timestamp和签名。子系统收到请求后,校验签名,清除本地会话。子系统的接口设计大概是这样的:

@PostMapping("/sso/logout") public Result<Boolean> ssoLogout(@RequestBody SSOLogoutReqVO reqVO) { // 1. 校验签名 ssoClient.verifySign(reqVO.getAppId(), reqVO.getTimestamp(), reqVO.getSign()); // 2. 清除本地会话 loginService.logoutByUserId(reqVO.getUserId()); return success(true); }

异步发送的安全性有一点要提醒:登出广播本来就是"尽力而为"的通知,不能因为某个子系统挂了就导致主站登出失败。所以我这里用线程池异步发送,并加了简单的重试机制,第一次发送失败后延迟 10 秒重试一次,再失败就只记录日志,由运维人员事后排查。这个设计在可靠性和实现成本之间取了平衡,如果你对登出强一致有要求,可以加消息队列,但很多中小项目确实没有必要。

3. 内嵌单点登录的三种实现方式与选型逻辑

标题里提到的"内嵌单点登录三种实现方式",是我在模块里同时支持的三种对接模式。很多项目在做 SSO 时只适配一种场景,我是因为在真实交付中发现客户子系统的形态差异实在太大,单一路线根本走不通,所以干脆做成三种模式,让接入方按自己的技术现状选择。

3.1 方式一:同主域 Cookie 共享模式

这是成本最低的一种方式,前提条件是所有子系统都在同一个顶级域名下。比如主站是auth.example.com,子系统是report.example.com、workflow.example.com,那么只需要在主域.example.com下种一个 Cookie,理论所有子域都能读到。

实现时有几个关键参数:

  • cookie.domain设置为.example.com
  • cookie.path设置为/
  • cookie.sameSite在生产环境建议设置为Lax,避免跨站请求带上 Cookie 导致安全问题
  • cookie.httpOnly必须为 true,防止 XSS 脚本读取会话标识

这种模式的优点是把登录态变成了浏览器行为,子系统只需要在本地解析 Cookie 里的用户标识,就能建立会话。但它的问题也很明显:一旦跨顶级域名,Cookie 就失效了。比如主站在example.com,子站是another.com,这条路就走不通。另一个隐患是安全边界较宽,所有子域只要能被攻击者控制一个,整个认证体系都会受影响。所以我的建议是:仅用于内部工具类系统,且主域名能统一管理的场景。

3.2 方式二:中心认证加票据重定向模式(CAS 风格)

这是yudao-module-sso默认推荐的模式,也是我前面时序图里描述的那种实现。它参考了 CAS 协议的核心思想,但做了裁剪:不直接实现完整的 CAS Ticket 校验协议,而是通过自己的校验接口完成登录态转换。

适用场景很清晰:子系统分布在不同的域名甚至不同的网络环境,无法共享 Cookie,但子系统自己有完整的用户体系和会话管理能力。这种模式对子系统的改造量最小,只需要增加一个"校验票据换用户"的接口,改动通常在半天以内就能完成。

票据重定向模式在内部还有一个变体:如果子系统只是前端需要登录,后端可以不做任何对接,由前端在 SSO 登录成功后把用户 token 直接放到本地 localStorage,前端每个请求带上 token。这种方式安全性稍弱,但对于纯前端静态部署的轻量系统很友好。我在项目中一般会根据子系统的后端能力来推荐具体做法,而不是一刀切。

3.3 方式三:OAuth2 授权码模式

当接入方不是内部系统,而是合作方、生态伙伴,或者需要更细粒度的授权控制时,我会启用占位在yudao-module-oauth2上的授权码模式。这个方式其实不算 yudao-module-sso 独有的能力,但我在 SSO 模块里做了一层适配,使它也能通过yudao_sso_app表里的应用配置直接接入。

流程上也就是标准的 OAuth2 授权码流程:跳转授权页、用户确认、回调授权码、换 token、拿用户信息。对于外部系统来说,这个模式的好处是有标准协议可参考,成熟 SDK 多;坏处是接入成本最高,参与方需要理解client_id、client_secret、redirect_uri、scope这一套概念。

比较有意思的场景是:同一个子系统可以同时支持票据模式和 OAuth2 模式。比如内部网络场景用票据模式,外部办公网络访问时走 OAuth2,这样既能享受到内网跳转的快捷,又能保证外部接入的可控性。

3.4 选型逻辑对比表格

三种模式放在一起做对比,我通常用下面这张表给客户讲,能省很多沟通成本:

维度Cookie 共享模式票据重定向模式OAuth2 授权码模式
接入成本低,基本不用改中,需要加校验接口高,需要理解完整协议
跨顶级域名不支持支持支持
客户端适配浏览器 Cookie 机制后端跳转+HTTP 调用标准 OAuth2 客户端
安全强度一般较高,一次性票据防重放高,可配置授权范围
适用场景内部同主域工具站群内部多域名系统合作方开放平台

选型时我的经验就一句话:离核心业务越远、域名越分散、接入方越不受控,就越需要往协议完整的方向走。纯内网、都是自己人的场景,根本没有必要让开发人员去理解授权码那套概念,票据模式就是效率和安全的最优平衡点。

4. 实操落地:pom 依赖、表结构初始化、核心代码与配置

4.1 模块创建与依赖关系

和 ruoyi-vue-pro 里其他模块一样,我会新建一个yudao-module-sso目录,并在pom.xml里声明依赖。核心依赖只需要三个:基础模块、system 的 API 模块、还有 Redis 缓存:

<dependency> <groupId>cn.iocoder.boot</groupId> <artifactId>yudao-common</artifactId> </dependency> <dependency> <groupId>cn.iocoder.boot</groupId> <artifactId>yudao-module-system-api</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>

这里有一个容易被忽略的点:不要直接依赖yudao-module-system的完整 jar,而是只依赖yudao-module-system-api。这样 SSO 模块只能通过 API 接口访问用户数据,不会和 system 模块内部的 Mapper、Service 耦合。模块化开发的边界感就在这种细节里。

4.2 配置项说明

在全局配置application.yaml中增加如下配置段:

yudao: sso: enabled: true login-url: /sso/login default-ticket-timeout: 300 cookie-domain: .example.com cookie-name: SSO_TOKEN logout-broadcast-enabled: true

配置项的含义我逐个说明:

  • login-url是统一登录入口,所有子应用跳转都会先进入这个地址。
  • default-ticket-timeout控制票据默认有效期,单位秒。我建议不要设太长,300 秒足够用户从登录页跳回子系统并完成校验,太长会增加被截获重放的风险窗口。
  • cookie-domain只在 Cookie 共享模式下生效,如果你全部走票据模式,这个配置可以留空。
  • logout-broadcast-enabled是登出广播开关,多子系统场景必须打开。

4.3 核心代码走读

票据生成的核心逻辑,我把它抽成了一个独立的SSOTicketService,这样 Controller 层只需要关注参数接收和跳转:

@Service public class SSOTicketService { @Resource private StringRedisTemplate stringRedisTemplate; public String createTicket(Long userId, String appId, int expireDuration) { String ticketId = UUID.randomUUID().toString().replace("-", ""); SSOTicket ticket = new SSOTicket(); ticket.setTicketId(ticketId); ticket.setUserId(userId); ticket.setAppId(appId); long expireTime = LocalDateTime.now().plusSeconds(expireDuration) .atZone(ZoneId.systemDefault()).toEpochSecond(); stringRedisTemplate.opsForValue().set( RedisKeyConstants.SSO_TICKET + ticketId, JSONUtil.toJsonStr(ticket), Duration.ofSeconds(expireDuration) ); return ticketId; } public UserInfo validateTicket(String appId, String ticketId) { String key = RedisKeyConstants.SSO_TICKET + ticketId; String value = stringRedisTemplate.opsForValue().get(key); if (StrUtil.isBlank(value)) { throw new ServiceException(SSOErrorCodeConstants.TICKET_INVALID); } SSOTicket ticket = JSONUtil.toBean(value, SSOTicket.class); if (!appId.equals(ticket.getAppId())) { throw new ServiceException(SSOErrorCodeConstants.TICKET_APP_MISMATCH); } // 验证通过后立即删除,防止重放 stringRedisTemplate.delete(key); return userApi.getUser(ticket.getUserId()); } }

这段代码里最容易被忽略的一行是stringRedisTemplate.delete(key)。很多早期做 SSO 的实现,就因为没有消费后删除,导致同一个 ticket 可以被抓包后反复使用。票据是一次性的,这个语义必须在代码级别强制执行,而不是靠业务约束。

签名校验的逻辑我放在客户端 SDK 里,服务端和客户端共用同一套签名算法:

public String buildSign(String appId, String timestamp, String appSecret) { String raw = appId + timestamp + appSecret; return DigestUtils.md5Hex(raw); }

签名的顺序必须是appId + timestamp + appSecret,并约定拼接顺序不参与 hash 碰撞讨论。它的作用是防止篡改和重放:timestamp 由接入方生成,超过 5 分钟则拒绝,签名不符则拒绝。这个机制简单但有效,内部系统完全不需要引入复杂的 JWT 和公钥体系。

4.4 管理端界面:给应用注册表补充运维页面

模块做出来了,如果管理端界面还是裸奔的,运维起来会非常痛苦。ruoyi-vue-pro 带代码生成器,所以我直接用它的生成功能把yudao_sso_app表的 CRUD 管理页面生成了出来,菜单名就叫"SSO 应用管理"。

生成后我重点调整了两个字段:

  • redirect_uris的录入形式改成了多行文本,一行一个回调地址,后端处理时按换行拆分,比逗号分隔更不容易出错。
  • appSecret在列表页不回显,只在创建或重置时显示一次,并增加"重置密钥"按钮,满足定期轮换密钥的要求。

界面这东西不必写得天花乱坠,但"密钥可以重置"这一条很重要。现实中密钥泄露是迟早的事,没有重置入口就只能手动改数据库,我可不想在工单里面看到"请帮我改一下 SSO 密钥"这种需求。

5. 踩坑记录与性能优化

5.1 回调 URL 编码与签名校验的坑

我最开始接入第一个子系统时,就遇到了回调地址带中文查询参数导致验证失败的问题。子系统的回调地址是https://report.example.com/callback?from=工作台,跳转时整个 URL 拼接在重定向参数后面,前端没有做 encodeURIComponent,结果到了 SSO 登录页时,from参数已经乱码,后续跳回去也跟着错。

这个问题的教训是:回调地址在拼接时,必须把整个 redirectUri 作为参数值进行一次 URL 编码。服务端在解析时再用URLDecoder.decode还原,然后拿还原后的地址和白名单比对。白名单比对这里也有讲究,不能只做前缀匹配,否则https://report.example.com/callback.attacker.com也能绕过,要按 URI 的协议、域名、端口、路径逐段做精确匹配。

5.2 Redis 序列化导致票据偶发校验失败

这个坑非常隐蔽。项目里的 Redis 配置本来用的是GenericJackson2JsonRedisSerializer,会把对象序列化成带类型信息的 JSON。我写SSOTicket时图省事,用了内部类或者没有提供无参构造函数,结果在反序列化时偶发报类型转换错误,ticket 明明存在但校验失败,用户反馈"登录跳转后白屏"。

排查过程花了半天,最后定位到问题后,我把票据存储的 key 和 value 改为纯 String 类型,value 只保存一个紧凑的 JSON,同时在SSOTicket上加上无参构造和 getter/setter。如果你也在 ruoyi-vue-pro 里集成 Redis 序列化,这一点要特别注意,最稳妥的方案是像上面代码那样用StringRedisTemplate专门处理票据,和业务对象的序列化体系隔离。

5.3 多个接入方同时回调时的性能问题

上线第一个月没出问题,直到公司举办了一场全员活动,几百人几乎同一时间从门户系统跳转到活动子系统,结果 SSO 模块的 CPU 瞬时拉高,用户跳转明显卡顿。我排查时发现瓶颈不在 Redis,而在校验接口里同步调用了用户 API 的完整信息查询,每个人都做了多级 DB 查询。

优化方案两个:第一,在SSOTicketService.validateTicket里只返回用户 ID、用户名、昵称这三个必要字段,不返回岗位、部门、角色这些重字段,减少 API 调用链;第二,对同一 userId 的用户信息增加本地缓存,TTL 设 5 分钟。登录审计日志的写入也从同步改成了异步。优化后单接口响应从 120ms 降到 20ms 左右,活动当天再没出现卡顿。

另外还有一个容易被忽略的小点:发现跳转慢的时候,要检查spring.mvc.async配置和 HTTP 连接池。如果子系统和 SSO 模块之间用的连接池默认最大连接数太小,并发稍微上来就会线程排队,表现为"偶发跳转超时"。适当调大连接池的maxTotal和maxIdle,同时对票据校验接口设置合理的超时时间,不要无限等下游。

5.4 前端登录页的 iframe 嵌入兼容问题

客户提出一个需求:希望把统一登录页嵌套在自己门户的 iframe 里,减少跳转的割裂感。这个需求听着简单,实际操作时到处都是问题。一个就是第三方 Cookie 限制:如果门户域名和 SSO 域名不一致,浏览器默认情况下会阻止 iframe 内的跨站 Cookie 写入,导致用户明明登录成功了,Cookie 却种不上。

实测后的处理办法:SSO 登录页检测到自身被 iframe 嵌套时,不再依赖 Cookie,而是登录成功后把 token 通过postMessage传递给父页面,由父页面保存并在后续路由中透传。这个方案有几个前提条件,一是父页面和子页面必须约定消息格式,二是要校验消息来源event.origin,不能什么域名发来的消息都信。如果你也要做类似功能,务必把postMessage的 origin 白名单校验写对,否则等于给 XSS 攻击开了一扇门。

6. 上线后的运行效果与后续扩展

6.1 一个真实系统的接入过程

让我用一个真实例子说明整个模块怎样降低了接入成本。客户有一个用 PHP 写的老报表系统,运维都准备弃坑了,但业务部门还在用,而且希望它能纳入统一登录。这个系统没有用户管理能力,之前是每半年手动同步一次账号密码。

接yudao-module-sso时,流程很顺利:

  1. 在管理后台注册应用,拿到 appId 和 appSecret,配置回调地址。
  2. 报表系统的登录入口改为跳转 SSO 的登录 URL,本地不再做账号密码验证。
  3. 报表系统新增一个 callback 接口:接收 ticket 后调用/sso/validate,拿到用户信息后,在本地建立一个临时会话记录,把用户 ID 映射到报表系统内部角色。
  4. 主站登出时,报表系统收到登出广播,销毁本地会话。

整个改造不到一个工作日,而且没有动报表系统的核心数据表。这是这个模块最让我满意的地方:它不是挑业务接入,而是让不同技术栈的系统都能用最低成本纳入统一认证。

6.2 后续还能怎么扩展

模块上线后,我下一个打算做的扩展是支持 OIDC 协议。虽然很多内网系统用现在的票据模式就够了,但随着外部合作伙伴增多,OIDC 的 ID Token 和标准用户信息端点会成为刚需。好消息是,现有模块的表结构已经在sso_mode字段上为多种模式留了空间,扩展时只需要新增一个控制器和解析器,不需要改已有的票据逻辑。

另外一个被问得比较多的方向是扫码登录。企业微信、钉钉的扫码登录,其实也可以理解为一种"外部 SSO 接入":用户扫码后,由外部平台回调到消息服务,再通过内部 SSO 流程创建会话。这个方向已经不在 yudao-module-sso 的本体范围里了,但能基于现有的应用注册 + 用户映射机制做得很顺。

从我个人的体会来说,模块化开发的真正价值,恰恰在于把一个横切关注点收拢成独立的领域模型。用户管理管的是"这是谁",OAuth2 管的是"能做什么",而 yudao-module-sso 管的是"如何让不同系统相信同一个用户的身份"。这三个问题分开思考、独立演进,比全部堆在一个模块里要清晰得多。如果你也准备在 ruoyi-vue-pro 上搭建单点登录,建议你也先把边界画清楚,再写代码,后面会省掉大量返工的麻烦。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询