最近遇到需要单点登录的场景,我使用的是芋道框架,正好它手动实现了OAuth2的功能,可以为单点登录提供一些帮助,结合授权码的模式,在改动最小的情况下实现了单点登录。关键业务数据已经隐藏,后续将以以主认证系统业务子系统的场景为例


一、主要流程

授权码模式(Authorization Code Grant)是OAuth 2.0标准中安全性最高的认证方式,主要通过“一次性授权码”避免敏感信息(如client_secret)暴露在前端。以下是主认证系统与业务子系统的单点登录全流程:


1. 用户触发跳转:从主系统到子系统的入口

用户在主认证系统的页面中点击“业务子系统入口”(例如“数据报表系统入口”),触发单点登录流程。这一步的核心是用户主动选择需要访问的子系统。


2. 主系统生成并返回授权码

前端调用主认证系统的授权接口/system/oauth2/authorize,并传递业务子系统的标识(如client_id=report-system)。主认证系统完成两项关键操作:

  • 验证用户状态:确认当前用户已登录主认证系统,且有权限访问目标子系统;
  • 生成一次性授权码:生成仅单次有效的随机字符串(如code=c36e714f324a43cfb9a75f24e14406c6),并拼接成跳转URL返回给前端。

返回的JSON示例如下:

{"code": 0,"data": "https://subsystem.example.com/login?code=c36e714f324a43cfb9a75f24e14406c6&state=1","msg": "成功"
}

关键设计:授权码仅单次有效,防止重放攻击;state参数用于防止CSRF攻击(本文示例简化为固定值,实际需动态生成)。


3. 前端重定向至子系统

前端通过浏览器重定向(302 Redirect)跳转到步骤2返回的URL(如https://subsystem.example.com/login?code=...)。此时,业务子系统的前端页面将接收到URL中的code参数,进入验证流程。


4. 子系统验证授权码并获取用户信息

业务子系统的核心任务是通过授权码向主认证系统验证其有效性,并获取用户身份信息。这一步必须由子系统后端完成(避免client_secret暴露在前端),具体流程如下:

4.1 子系统前端传递code至后端

前端从URL中提取code参数,调用子系统后端接口/login/callbackLogin,将code传递给后端。

4.2 后端调用主系统验证接口

子系统后端通过HTTP请求调用主认证系统的/system/oauth2/token接口,传递以下参数:

  • client_id:子系统标识(如report-system);
  • client_secret:子系统密钥(需保密,仅后端持有);
  • grant_type=authorization_code:标识使用授权码模式;
  • code:步骤2生成的授权码;
  • redirect_uri:登录成功后的跳转地址(需与主认证系统预先配置一致)。

主认证系统验证通过后,返回用户信息及访问令牌(access_token),示例如下:

{"code": 0,"data": {"scope": "all","userId": 194,"subsystemCode": "report-system","subsystemName": "数据报表系统","access_token": "f963b902248646ffa71d27cdc48fd37d","refresh_token": "8d4dfc224e724ceca296c40b2087f7c7","token_type": "bearer","expires_in": 1799},"msg": "成功"
}
4.3 子系统完成用户登录

子系统后端通过返回的userIdsubsystemCode查询本地用户信息(若用户不存在需提前同步或注册),生成子系统的登录令牌(如JWT),并记录登录日志。

关键代码示例(子系统后端)

public AuthLoginRespVO callbackLogin(String code) throws IOException {// 1. 调用主认证系统,用code换取用户标识(如subsystemCode)String userIdentifier = oAuth2TokenClient.getUserIdByAuthCode(code);// 2. 根据用户标识查询本地用户(需提前维护主系统与子系统的用户映射)AdminUserDO localUser = userService.getUserByIdentifier(userIdentifier);if (localUser == null) {throw ServiceExceptionUtil.exception(USER_NOT_EXISTS, "用户未同步至子系统");}// 3. 生成子系统登录令牌,记录日志return createTokenAfterLoginSuccess(localUser.getId(), localUser.getUsername(), LoginLogTypeEnum.LOGIN_SSO);
}

5. 子系统生成令牌并跳转首页

子系统后端将生成的登录令牌(如token=abc123)返回给前端,前端携带该令牌跳转到子系统首页,完成单点登录。


二、子系统后端的HTTP客户端实现

子系统后端需要通过HTTP客户端与主认证系统交互,以下是核心实现类(已简化):

@Component
public class OAuth2TokenClient {// 从配置文件读取主认证系统信息(敏感信息需加密存储)@Value("${sso.base_url}")private String baseUrl; // 主认证系统基础URL(如http://sso.main-system.com)@Value("${sso.token_url}")private String tokenUrl; // 令牌接口路径(如/system/oauth2/token)@Value("${sso.client_id}")private String clientId; // 子系统标识@Value("${sso.client_secret}")private String clientSecret; // 子系统密钥(需保密)@Value("${sso.redirect_url}")private String redirectUri; // 登录成功跳转地址private static final ObjectMapper objectMapper = new ObjectMapper();public String getUserIdByAuthCode(String authCode) throws IOException {// 构造POST请求HttpPost httpPost = new HttpPost(baseUrl + tokenUrl);httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");// 组装请求参数(严格遵循OAuth 2.0规范)List<NameValuePair> params = new ArrayList<>();params.add(new BasicNameValuePair("client_id", clientId));params.add(new BasicNameValuePair("client_secret", clientSecret));params.add(new BasicNameValuePair("grant_type", "authorization_code"));params.add(new BasicNameValuePair("code", authCode));params.add(new BasicNameValuePair("redirect_uri", redirectUri));try (CloseableHttpClient client = HttpClients.createDefault();CloseableHttpResponse response = client.execute(httpPost)) {String responseBody = EntityUtils.toString(response.getEntity());JsonNode root = objectMapper.readTree(responseBody);// 校验主认证系统返回状态if (root.path("code").asInt() != 0) {throw ServiceExceptionUtil.exception(new ErrorCode(root.path("code").asInt(), root.path("msg").asText()));}// 提取用户标识(根据主认证系统返回结构调整)return root.path("data").path("subsystemCode").asText();} catch (ParseException e) {throw new RuntimeException("响应解析失败", e);}}
}
查看全部

三、配置示例

sso:base_url: "http://sso.main-system.com" # 主认证系统基础URL(替换为实际地址)token_url: "/system/oauth2/token" # 授权码验证接口路径client_id: "report-system" # 子系统标识(需主认证系统预先注册)client_secret: "subsystem-secret-123" # 子系统密钥(需加密存储,避免明文)redirect_url: "http://subsystem.example.com/auto-login" # 登录成功跳转地址(需与主认证系统配置一致)

四、子系统前后端协作流程总结

阶段前端操作后端操作
接收code从URL参数中提取code-
传递code调用/login/callbackLogin接口,传递code接收code,调用主认证系统验证
完成登录接收后端返回的token生成子系统token,返回前端
跳转首页携带token跳转到首页-

五、注意事项

  1. 授权码的安全性
    • 授权码仅单次有效,主认证系统需严格校验其使用状态,防止重放攻击;
    • 避免在前端暴露client_secret,所有与主认证系统的交互必须由后端完成。
  2. 用户映射与同步
    • 主认证系统与子系统需维护用户关联关系(如主系统userId=194对应子系统userId=1001),建议通过定时任务或事件通知同步用户信息;
    • 若用户未同步至子系统,需明确提示“用户无权限”或触发自动注册流程(需评估安全风险)。
  3. 错误处理
    • 主认证系统返回错误(如code无效)时,子系统需捕获异常并返回友好提示(如“登录失败,请重新操作”);
    • 记录详细的日志(如code、请求时间、错误码),便于排查问题。
  4. 参数校验
    • 子系统后端需校验code的格式(如长度、字符类型),防止非法请求;
    • state参数需动态生成并校验(本文示例简化,实际需实现),防止CSRF攻击。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/87651.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/87651.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/87651.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于Seata的一个小issue...

文章目录 引言原因&#x1f913;解决方法&#x1f635;总结❤️ 引言 某一天&#xff0c;笔者在逛着Github的时候&#xff0c;突然看到seata有个有趣的issue&#xff0c;是一个task。 相关描述&#xff1a; While running the DruidSQLRecognizerFactoryTest.testIsSqlSynta…

FTTR+软路由网络拓扑方案

文章目录 网络拓扑软路由配置FTTR光猫路由器TPLink路由器配置WAN设置LAN设置 参考 网络拓扑 软路由配置 配置静态IP地址&#xff1a;192.168.1.100设置网关指向主路由的IP 设置自定义DNS服务器 开启DHCP 这一步很关键&#xff0c;可以让连上wifi的所有设备自动趴强。 FTTR光猫…

RPC - 服务注册与发现模块

为什么要服务注册&#xff0c;服务注册是做什么 服务注册主要是实现分布式的系统&#xff0c;让系统更加的健壮&#xff0c;一个节点主机将自己所能提供的服务&#xff0c;在注册中心进行登记 为什么要服务发现&#xff0c;服务发现是要做什么 rpc调用者需要知道哪个节点主机…

分布式缓存:应对突发流量的缓存体系构建

文章目录 缓存全景图Pre背景与目标说明缓存原则与设计思路缓存体系架构缓存预热与缓存预加载库存操作与缓存结合防刷、限流与缓存缓存一致性与失效异步落地与消息队列监控与指标容灾与扩展示例小结 缓存全景图 Pre 分布式缓存&#xff1a;缓存设计三大核心思想 分布式缓存&am…

华为云Flexus+DeepSeek征文|CCE容器高可用部署搭建Dify-LLM平台部署AI Agent

华为云FlexusDeepSeek征文&#xff5c;CCE容器高可用部署搭建Dify-LLM平台部署AI Agent 前言 Dify是一款开源的大语言模型应用开发平台&#xff0c;融合了后端即服务和LLMOps的理念&#xff0c;使开发者可以快速搭建生产级的生成式AI应用&#xff0c;本文将详细介绍如何使用华…

Postman 的 Jenkins 管理 - 手动构建

目录 一、准备工作 二、postman 项目脚本准备并导出 1. 打开已完成并测试无误的 postman 项目脚本。 再次执行测试。 ​编辑2. 导出&#xff08; 测试用例集、环境变量 两个文件&#xff09;**“不 支 持 中 文”** —— 全部改成英文&#xff01; ​编辑3. 文件所在目录…

音视频之H.264/AVC解码器的原理和实现

系列文章&#xff1a; 1、音视频之视频压缩技术及数字视频综述 2、音视频之视频压缩编码的基本原理 3、音视频之H.264/AVC编码器原理 4、音视频之H.264的句法和语义 5、音视频之H.264/AVC解码器的原理和实现 6、音视频之H.264视频编码传输及其在移动通信中的应用 7、音视…

【智能安全帽新升级】搭载VTX316TTS语音合成芯片,让安全“听得见”!

在工地轰鸣的机械声中&#xff0c;一句清晰的指令可能比任何文字都更有力量。 当智能安全帽遇上VTX316语音合成芯片&#xff0c;安全防护从“被动响应”进化为“主动交互”&#xff0c;为高危行业戴上了一顶“会说话的智慧大脑”&#xff01; 传统安全帽的“沉默”危机 在建筑…

【目标检测】非极大值抑制(NMS)的原理与实现

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

DB-GPT启动提示please install by running `pip install cryptography`

DB-GPT项目需要 cryptography 库来处理加密功能&#xff0c;但环境中没有安装它。cryptography 是一个用于安全和加密操作的Python库&#xff0c;许多项目&#xff08;包括DB-GPT&#xff09;依赖它来处理敏感数据的加密存储。 解决方案 1. 安装 cryptography 库 在激活的环…

局域网文件共享及检索系统

标题:局域网文件共享及检索系统 内容:1.摘要 随着信息技术的飞速发展&#xff0c;局域网在企业、学校等场景中得到广泛应用&#xff0c;大量文件在局域网内存储和流转。然而&#xff0c;目前局域网内文件共享与检索存在效率低、管理困难等问题。本文旨在设计并实现一个高效的局…

Spring Boot医疗系统高并发难题:达梦数据库死锁排查与优化实战

Spring Boot医疗系统高并发难题:达梦数据库死锁排查与优化实战 引言:医疗系统中的并发挑战 在现代医疗系统中,检查申请处理是关键业务场景之一,每天需要处理数以万计的检查记录。当多个操作同时更新同一患者的申请状态时,数据库层面的死锁问题成为高并发环境下的典型痛点…

Go语言中的文件与IO:bufio 和 scanner

Go 标准库中的 bufio 包提供了带缓冲的读写功能&#xff0c;可以显著提高文件和数据处理效率。而 bufio.Scanner 则是读取文本文件中每一行的利器&#xff0c;常用于日志、配置等文本处理场景。 一、为什么使用 bufio&#xff1f; 直接对文件进行 os.File.Read() 或 os.File.W…

ABP微服务架构中网关层NullReferenceException问题解析与HTTP配置优化

ABP微服务架构中网关层NullReferenceException问题解析与HTTP配置优化 一、网关层System.NullReferenceException问题解析 1.1 问题现象与原因分析 在ABP微服务架构开发过程中&#xff0c;网关层启动后调用微服务接口时出现以下异常&#xff1a; System.NullReferenceExcep…

啊啊啊啊啊啊啊啊code

前序遍历和中序遍历构建二叉树 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNod…

【算法 day06】LeetCode 454.四数相加II | 15. 三数之和 | 18. 四数之和

454.四数相加II 题目链接 | 文档讲解 |视频讲解 : 链接 1.思路&#xff1a; 0.定义一个count&#xff0c;计算最终出现的次数 1.先遍历nums1和nums2,求出两者的和&#xff0c;map的key是和&#xff0c;value是出现的次数 2.再遍历nums3和nums4&#xff0c;求出0-两者的和 3…

【项目实训】【项目博客#09】HarmonySmartCodingSystem系统后端智能API检索与代码助手实现(6.2-6.15)

【项目实训】【项目博客#09】HarmonySmartCodingSystem系统后端智能API检索与代码助手实现&#xff08;6.2-6.15&#xff09; 文章目录 【项目实训】【项目博客#09】HarmonySmartCodingSystem系统后端智能API检索与代码助手实现&#xff08;6.2-6.15&#xff09;项目博客概述一…

【JVM】- 类加载与字节码结构3

类加载阶段 1. 加载 加载&#xff1a;将类的字节码载入方法区中&#xff0c;内部采用C的instanceKlass描述java类。如果这个类的父类还没加载&#xff0c;则先加载父类加载和链接可能是交替运行的 通过全限定名获取字节码 从文件系统&#xff08;.class 文件&#xff09;、JA…

Qt蓝图式技能编辑器状态机模块设计与实现

设计概述 这个模块是一个基于Qt的蓝图式技能编辑器状态机&#xff0c;主要用于游戏开发中的技能状态管理。核心功能包括&#xff1a; 状态节点&#xff08;开始、结束、普通状态&#xff09;的可视化 状态间连线的绘制与管理 状态转换逻辑的可视化编辑 动作选择与配置 核…

Unity AR识别物体的内容语音读取+使用说明功能

因之前一直在开发项目&#xff0c;断断续续写了一点博客&#xff0c;最后统一写了一下博客记录学习内容。 可以看到我的工作一直在进行。 目录 一、识别内容语音读取 二、点击齿轮按钮弹出使用说明界面 开发步骤 1. 创建齿轮按钮 UI 2. 创建使用说明面板 UI 3. 编写控制…