1. 方案背景
在企业级微服务架构中,经常存在"平台侧"与"业务侧"分离的场景。本方案针对典型的流程办理系统集成场景:
- A系统(平台侧):负责流程流转、任务分发。
- B系统(业务侧):负责具体的业务表单开发与数据处理。
业务需求:用户在A系统点击"办理"时,需要无缝跳转至B系统的表单页面,并安全传递当前用户身份、流程实例ID(ProcessID)、任务节点ID(TaskID)等关键上下文信息。
2. 核心挑战与设计目标
在实现上述集成时,传统方案面临以下挑战:
- 数据泄露风险 :直接通过URL参数(如
?userId=123&processId=456)传递敏感信息,极易在浏览器历史记录、代理服务器日志或Referer头中泄露。 - 数据篡改风险:明文参数可被恶意用户修改,导致非法操作或越权访问。
- 时效性控制:链接若长期有效,存在被截获重放的风险。
- 架构复杂度:引入分布式缓存进行Token状态管理会增加系统耦合度和运维成本。
设计目标:
- 机密性/完整性:根据数据敏感度,提供可配置的加密或签名保护。
- 无状态:B系统无需依赖外部存储组件,实现轻量级解耦。
- 高安全:通过极短的有效期窗口,从时间维度杜绝重放攻击。
3. 解决方案:基于 JWT 的超短有效期 Token
本方案采用 JWT (JSON Web Token) 家族技术,结合 超短有效期 TTL (20-30秒) 的技术路线。
根据业务数据的安全级别,我们提供两种实现模式:
3.1 技术选型:JWE vs JWT (JWS)
在具体实施时,应根据参数的敏感程度选择具体的 JWT 封装格式:
-
模式一:JWE (JSON Web Encryption) ------ 高敏感数据
- 机制 :对 Payload 进行加密。
- 适用场景 :传递用户ID、手机号、身份证号、内部业务密钥等敏感信息。
- 特性:即使攻击者截获URL,也无法解密内容,确保了数据在传输链路中的绝对机密性。
-
模式二:JWT (JWS) ------ 非敏感/可公开数据
- 机制 :对 Payload 进行签名(Base64编码 + 签名)。
- 适用场景 :传递流程ID、表单ID、订单号等业务公开标识,且评估这些信息泄露不会造成直接安全风险。
- 特性:数据对浏览器可见(Base64可解码),但无法被篡改。性能优于 JWE(无加解密开销)。
3.2 通用策略:20-30秒超短 TTL
无论选择 JWE 还是 JWT (JWS),均采用 20-30秒 的超短有效期:
- Token从生成到跳转完成,通常仅需 1-3 秒。
- 设置 20-30 秒有效期,足以覆盖网络延迟和用户操作耗时。
- 天然防重放:由于有效期极短,攻击者截获Token后进行重放的时间窗口几乎不存在,因此无需引入分布式存储记录已使用的Token,实现了真正的无状态服务。
3.3 交互流程
- 发起请求:用户在A系统点击"办理"。
- 生成令牌 :A系统后端根据数据敏感度,选择 JWE 加密或 JWS 签名,封装上下文信息,设置
exp=当前时间+20~30秒。 - 重定向跳转 :A系统返回 302 重定向,指向 B 系统表单地址,携带处理后的
ticket参数。 - 解密/验签 :B系统后端拦截请求,解析
ticket。 - 上下文注入:校验通过(未超时、签名有效/解密成功)后,B系统提取信息建立本地会话上下文,渲染表单。
3.4 时序图
B系统 (业务侧) A系统 (平台侧) 用户 B系统 (业务侧) A系统 (平台侧) 用户 alt [校验成功] [校验失败 (超时/篡改)] 点击"办理" 1 1. 评估数据敏感度 选择 JWE 或 JWT (JWS) 2 2. 封装并处理数据 (TTL = 20-30秒) 3 3. 重定向至B系统URL (https://sys-b.com/form?ticket=xxx) 4 4. 请求B系统表单 5 5. 解密或验签 Token 并且校验有效期 6 6. 建立本地会话上下文 7 7. 渲染业务表单 8 8. 400/401 错误提示 9
4. 技术实现要点
本文使用 Java 作为示例语言(基于 JJWT 0.12.x 库),但 JWT/JWE 是通用的算法标准,同样适用于其他编程语言。
4.1 A系统:生成与跳转
A系统根据策略生成 Token。以下代码以 JWE (加密模式) 为例,若使用 JWT (JWS),只需将 encryptWith 替换为 signWith。
java
// 核心逻辑:使用 A256GCM 算法加密 (适用于敏感数据)
String ticket = Jwts.builder()
.claims(Map.of(
"userId", authentication.getName(), // 敏感信息
"processId", processInstanceId
))
.expiration(Date.from(Instant.now().plusSeconds(30))) // TTL 20-30秒
.encryptWith(secretKey, Jwts.ENC.A256GCM)
.compact();
// 若数据不敏感,可改用 JWS (签名模式),性能更高:
// String ticket = Jwts.builder()
// .claims(...)
// .expiration(...)
// .signWith(secretKey, Jwts.SIG.HS256) // 仅签名
// .compact();
// 重定向
response.sendRedirect("https://system-b.com/form?ticket=" + ticket);
4.2 B系统:解析与鉴权
B系统作为数据消费方,通过过滤器在请求入口处进行统一处理。根据事先约定的方式(JWE 或 JWS)选择对应的解析逻辑。
java
// 方式一:解密 JWE(适用于敏感数据)
Jws<Claims> jws = Jwts.parser()
.decryptWith(secretKey)
.build()
.parseEncryptedJws(ticket);
Claims claims = jws.getPayload();
// 方式二:验签 JWT/JWS(适用于非敏感数据)
// Jws<Claims> jws = Jwts.parser()
// .verifyWith(secretKey)
// .build()
// .parseSignedClaims(ticket);
// Claims claims = jws.getPayload();
// 后续处理:
// 1. 将 claims 中的用户信息(如 userId)设置到当前系统的会话上下文中
// 2. 将业务参数(如 processId)传递给业务层使用
5. 方案优势分析
| 维度 | 传统 URL 参数 | JWT (JWS) 签名模式 | JWE 加密模式 |
|---|---|---|---|
| 数据安全性 | 明文传输,极高风险 | Base64编码,内容可见 | 密文传输,内容不可见 |
| 防篡改能力 | 无,易伪造 | 强,依赖数字签名 | 强,包含签名校验 |
| 性能开销 | 最低 | 低 (仅签名计算) | 理论是比JWT高实际上几乎没有差异 (混合加密:密钥非对称加密 + 数据对称加密) |
| 适用场景 | 仅限完全公开的非关键参数 | 非敏感业务参数 (如订单号、流程ID) | 敏感隐私参数 (如用户ID、手机号) |
| 防重放机制 | 无 | 20-30秒 TTL,天然防重放 | 20-30秒 TTL,天然防重放 |
6. 为什么使用 302 而不是 301?
在实现 A 系统跳转到 B 系统时,必须使用 HTTP 302 Found (临时重定向),而不能使用 301 Moved Permanently(永久重定向)。
核心原因
浏览器缓存机制的根本差异:
- 301 (永久重定向) :浏览器会长期缓存重定向关系。用户首次访问后,后续请求会直接跳过 A 系统访问 B 系统,导致 A 系统无法生成新 Token,用户携带过期参数导致业务失败。
- 302 (临时重定向) :浏览器默认不缓存(或缓存时间极短)。每次用户点击"办理",浏览器都会向 A 系统发起请求,A 系统实时生成最新 Token 并重定向。
配合本方案的 20-30 秒超短有效期,使用 301 会导致用户第二次点击时复用旧链接(Token 已过期),只有 302 才能确保每次跳转都是新的有效授权。
最佳实践
为彻底避免缓存问题,建议在 302 响应中添加缓存控制头:
java
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.setHeader("Pragma", "no-cache");
7. 风险评估与应对策略
尽管该方案架构极简,但在实施时需注意以下关键点:
7.1 时钟同步
- 基础要求 :服务器时间与标准时间(如北京时间)保持一致是任何生产系统的运维基准。所有服务器应配置统一的 NTP 时间源,将时钟误差控制在合理范围内(通常建议 1-5 秒)。
- 本方案的特殊性:由于采用 20-30 秒的超短有效期,对时钟同步的容忍度较低。若 A、B 服务器时间不同步(例如 A 比 B 快 15 秒),可能导致用户合法请求被判定为已过期。
- 应对 :确保 A、B 系统使用同一套 NTP 时间源 ,在基础运维要求之上,建议将时钟误差控制在 3 秒以内,为本方案预留足够的安全余量。
7.2 URL 长度限制
- 现状 :现代浏览器对 URL 长度的限制已非常宽松(通常 2MB 以上),实际限制主要在服务端。常见的 Web 服务器(如 Nginx)默认限制约 4KB-8KB,应用服务器(如 Tomcat)也有类似配置。
- 建议 :建议 Payload 数据内容 控制在 2000 字符以内 。JWE/JWT 处理后会增加约 30%-50% 的长度,最终 URL 长度仍在服务端安全范围内。2000 字符足以覆盖 99% 的业务场景(仅需传递少量 ID,无需传递长文本)。
- 应对 :Payload 极简原则 。Token 中仅传递 ID(如
userId,processId),不传递名称、详情等长文本。B 系统解析 ID 后,自行通过数据库查询详细信息。
7.3 密钥管理
- 对称密钥(如 HMAC/AES) :A、B 系统共享同一密钥,任一系统泄露密钥则全线崩塌。需确保密钥在配置中心、环境变量中保密存储,禁止硬编码或明文存储。
- 非对称密钥(如 RSA/ECC) :A 系统使用 B 系统的公钥 加密/签名,B 系统使用自己的私钥解密/验签。私钥仅在 B 系统内部保存,降低了密钥泄露的风险面。
8. 结论
本方案提供了基于 JWT (JWS) 和 JWE 的灵活集成策略。通过引入 20-30秒超短有效期 TTL ,在确保数据防篡改的前提下,根据数据敏感度平衡了性能与安全性。同时,该方案成功移除了对分布式状态存储的依赖,是一种轻量级、高安全性、低耦合的跨系统集成方案。
单点登录场景扩展
除本文重点介绍的业务参数传递场景外,本方案同样适用于轻量级单点登录(SSO):
- 用户已在 A 系统登录,跳转到 B 系统时自动完成登录
- 将用户身份信息(如
userId、username)封装到 Token 中 - B 系统解析 Token 后建立本地会话,无需用户重复输入凭证
相比复杂的 SSO 协议(如 CAS、SAML、OAuth2),本方案更加简洁,适用于企业内部系统间的快速集成。
(END)