一、集成平台是什么?(小白秒懂版)
1.1 简介
想象一下:医院里有挂号处 、诊室 、药房三个地方。
-
传统方式:你要先去挂号处拿号 → 自己找诊室 → 自己找药房(容易迷路)
-
集成平台方式:你只告诉前台(集成平台)需求 → 前台带你去每个地方(安全又省心)
- "应用集成平台充当认证中心和授权中心,协助系统间安全传递业务参数。"
- 应用集成平台只做"桥梁",不替代各系统自己的功能!
一句话总结 : 集成平台 = 系统间的"前台服务员",帮你安全地从A系统走到B系统!
1.2 安全机制
-
Token认证 :所有接口调用都需要有效的Token
-
SM4加密 :数据传输使用SM4加密保护
-
防重复请求 :使用Redisson分布式锁防止重复请求
-
黑名单验证 :系统/IP级别的黑名单验证
-
参数验证 :严格的参数格式和有效性验证
1.3 技术实现关键点
-
Redis缓存 :Token(5小时)、Ticket(300秒)、会话ID等缓存
-
Kafka消息 :使用主题 IAP_TOPIC_${servCode} 进行消息广播
-
分布式锁 :Redisson锁防止重复请求
-
事务管理 :@Transactional注解确保数据一致性
-
异常处理 :统一的异常处理机制
二、集成平台能做什么?(三大场景)
| 场景 | 适用情况 | 例子 | 需要哪些接口 |
|---|---|---|---|
| 1. 页面重定向 | 需要浏览器跳转到另一个系统页面 | 从挂号系统跳转到互联网医院问诊页 | 申请Token + 申请授权码(A系统地址重定向) + 认证授权(B系统校验授权码) |
| 2. HTTP API调用 | 需要后台获取数据(用户无感) | 互联网医院调用挂号系统的科室列表 | 申请Token + 服务调用 |
| 3. 消息广播 | 需要通知多个系统同一件事 | 挂号系统发布"新号源",通知所有相关系统 | 申请Token + 发布消息 + 确认消息 |
✅ 记住这个判断法:
有页面跳转 ? → 用场景1
需要后台取数据 ? → 用场景2
要群发通知 ? → 用场景3 绝不混用!
2.1 核心组件
-
IapRuntimeController.java :核心运行时控制器,处理所有接口请求
-
服务层 :实现业务逻辑(订阅验证、token管理等)
-
数据访问层 :使用MyBatis-Plus操作数据库
-
缓存层 :Redis用于token、ticket等缓存
-
消息队列 :Kafka用于消息广播
2.2 主要接口
| 接口地址 | 功能描述 | 请求方法 |
|---|---|---|
/interface/v1/token |
申请访问令牌 | POST |
/interface/v1/preauth |
页面重定向预授权(申请授权码) | POST |
/interface/v1/authentication |
页面重定向认证(认证授权) | POST |
/runtime/v1/** |
服务调用 | POST |
/interface/v1/publish/message |
消息广播发布 | POST |
三、前期准备:必须知道的基础知识
3.1 系统编码表--- 每个系统都有"身份证号"
| 系统名称 | 编码 | 你的身份 |
|---|---|---|
| 挂号平台 | 100 | 如果你是挂号系统,记下100 |
| 互联网医院 | 101 | 如果你是互联网医院,记下101 |
| 百度灵医 | 901 | 常见能力系统 |
| 支付宝 | 902 | 常见能力系统 |
| 集成平台 | 999 | 所有请求都要知道它 |
| 护理平台 | 102 | 其他业务系统 |
📌 重要:每次调用接口,请求头必须携带你的系统编码!
3.2 通信规则
-
所有数据都加密:使用SM4加密算法(像给信件加密码锁)
-
请求格式:
javascript{ "param": "加密后的业务数据" } -
响应格式:
javascript{ "code": "200", "message": "成功", "data": "加密后的返回数据" }
3.3安全要求
每个请求必须做到:
在请求头写入
systemCode(你的系统编码)生成24位唯一
messageId(不能重复!)在加密的param中再次写入相同的messageId
除Token接口外,其他接口必须携带
token参数
四、三大场景详细操作指南(带示例)
场景1:页面重定向(从A系统跳到B系统)
**典型业务:**挂号系统(100) → 跳转到 互联网医院(101)
必须接口:`/iap/interface/token` + `/iap/interface/v1/preauth` + `/iap/interface/v1/authentication`
javascript
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 系统B │ │ IAP平台 │ │ 系统A │
└─────┬───────┘ └─────┬───────┘ └─────┬───────┘
│ │ │
│ 1. 申请Token │ │
│───────────────────>│ │
│ │ │
│ 2. 返回Token │ │
│<───────────────────│ │
│ │ │
│ 3. 预授权请求 │ │
│───────────────────>│ │
│ │ 4. 验证Token │
│ │───────────────────>│
│ │ │
│ │ 5. 生成Ticket和Session│
│ │───────────────────>│
│ │ │
│ 6. 返回重定向URL │ │
│<───────────────────│ │
│ │ │
│ 7. 重定向到系统A │ │
│────────────────────────────────────────>│
│ │ │
│ │ 8. 验证Ticket │
│ │<───────────────────│
│ │ │
│ │ 9. 返回Session参数 │
│ │───────────────────>│
┌─────┴───────┐ ┌─────┴───────┐ ┌─────┴───────┐
│ 系统B │ │ IAP平台 │ │ 系统A │
└─────────────┘ └─────────────┘ └─────────────┘
1. 申请Token :系统B调用 /interface/v1/token 接口获取Token 2. 预授权 :系统B调用 /interface/v1/preauth 接口,提供servCode等参数 3. 生成Ticket :IAP生成16位ticket,有效期300秒,存储在Redis中 4. 保存Session :IAP保存会话信息到 iap_session 表,参数到 iap_session_serv_param 表 5. 返回重定向URL :IAP返回包含sessionId、ticket和redirectUri的响应 6. 重定向 :系统B重定向到系统A的页面 7. 验证Ticket :系统A调用IAP的 /interface/v1/authentication 接口验证ticket 8. 返回Session参数 :IAP返回会话参数给系统A
步骤1:A系统(挂号系统)获取Token
-
接口 :
POST /iap/interface/token -
请求头:
javascriptsystemCode: 100 messageId: 1719225600100099903011234(24位唯一ID) -
请求体(加密后):
javascript{ "clientId": "system_100", "clientSecret": "你的密钥" } -
返回(解密后):
javascript{ "token": "Token_100_abc123", "sessionId": "session_123" }
步骤2:A系统申请跳转凭证(关键步骤!)
-
接口 :
POST /iap/interface/v1/preauth -
请求头:
javascriptsystemCode: 100 messageId: 1719225600100099903025678(新ID) token: Token_100_abc123(上一步获得) -
请求体(加密后):
javascript{ "messageId": "1719225600100099903025678", "servCode": "online_hospital", "param": { "user": { "systemUserId": "123", "systemUserName": "张三" } } } -
返回(解密后):
javascript{ "redirectUrl": "http://hospital.com/chat", "ticket": "ticket_xyz789", "timestamp": "1719225600", "sessionId": "preauth_456" }
步骤3:A系统跳转到B系统
-
操作:浏览器重定向到
csshttp://hospital.com/chat?ticket=ticket_xyz789×tamp=1719225600&presessionid=preauth_456
步骤4:B系统(互联网医院)验证跳转
-
接口 :
POST /iap/interface/v1/authentication -
请求头:
javascriptsystemCode: 101 messageId: 1719225600101099903039012(新ID) token: Token_101_def456(B系统自己的Token) -
请求体:请求集成平台验证ticket是否有效
javascript{ "ticket": "ticket_xyz789", "timestamp": "1719225600" } -
返回:用户信息等业务参数
✅ 场景1完整流程: A系统拿Token → A系统申请跳转凭证 → 浏览器跳转 → B系统验证凭证 → 进入业务页面
场景2:HTTP API调用(后台获取数据)
典型业务:互联网医院(101)调用挂号系统(100)的科室列表
必须接口 :/iap/interface/token + /iap/runtime/v1/{uri}
javascript
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 系统B │ │ IAP平台 │ │ 系统A │
└─────┬───────┘ └─────┬───────┘ └─────┬───────┘
│ │ │
│ 1. 申请Token │ │
│───────────────────>│ │
│ │ │
│ 2. 返回Token │ │
│<───────────────────│ │
│ │ │
│ 3. 调用服务(带Token)│ │
│───────────────────>│ │
│ │ 4. 验证Token │
│ │───────────────────>│
│ │ │
│ │ 5. 验证订阅关系 │
│ │───────────────────>│
│ │ │
│ │ 6. 转发请求 │
│ │───────────────────>│
│ │ │
│ │ 7. 返回响应 │
│ │<───────────────────│
│ │ │
│ 8. 返回加密响应 │ │
│<───────────────────│ │
┌─────┴───────┐ ┌─────┴───────┐ ┌─────┴───────┐
│ 系统B │ │ IAP平台 │ │ 系统A │
└─────────────┘ └─────────────┘ └─────────────┘
服务发布(系统A)
在 iap_serv 表配置服务(状态=ENABLE)
在 iap_serv_http_api 表配置API信息(请求URI、方法等)
服务订阅(系统B订阅系统A对应的服务)
调用 IapServSubscribeController.create() 接口
保存订阅关系到 iap_serv_subscribe 表
描述:
申请Token :系统B调用 /interface/v1/token 接口,获取有效期为5小时的Token
调用服务 :系统B使用Token和加密参数调用 /runtime/v1/** 接口
验证Token :IAP验证Token的有效性和系统匹配性
验证订阅关系 :IAP检查系统B是否已订阅该服务
转发请求 :IAP将请求转发给系统A
返回响应 :系统A返回响应,IAP加密后返回给系统B
步骤1:B系统(互联网医院)获取Token
-
接口 :
POST /iap/interface/token -
请求头:
javascriptsystemCode: 101 messageId: 1719225600101099903043456(24位ID) -
请求体(加密后):
javascript{ "clientId": "system_101", "clientSecret": "你的密钥" } -
返回:
javascript{ "token": "Token_101_def456", "sessionId": "session_789" }
步骤2:B系统调用服务
-
接口 :
POST /iap/runtime/v1/departments请求头:
javascriptsystemCode: 101 messageId: 1719225600101099903057890(新ID) token: Token_101_def456(上一步获得)请求体(加密后):
javascript{ "messageId": "1719225600101099903057890", "serviceCode": "get_departments", "param": { "hospitalId": "123456" } } -
返回(解密后):
javascript{ "departments": [ {"id": "1", "name": "内科"}, {"id": "2", "name": "外科"} ] }
✅ 场景2完整流程: B系统拿Token → B系统直接调用服务 → 获取数据
注意 :此场景不调用
/iap/interface/v1/preauth(申请授权码)和/iap/interface/v1/authentication(认证授权)
场景3:消息广播(群发通知)
典型业务:挂号系统(100)发布"新号源",通知所有订阅系统
必须接口 :/iap/interface/token + /iap/interface/v1/publish/message + /iap/interface/v1/commit/message
javascript
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 系统A │ │ IAP平台 │ │ Kafka │
└─────┬───────┘ └─────┬───────┘ └─────┬───────┘
│ │ │
│ 1. 申请Token │ │
│───────────────────>│ │
│ │ │
│ 2. 返回Token │ │
│<───────────────────│ │
│ │ │
│ 3. 发布消息请求 │ │
│───────────────────>│ │
│ │ 4. 验证Token │
│ │───────────────────>│
│ │ │
│ │ 5. 验证发布权限 │
│ │───────────────────>│
│ │ │
│ │ 6. 发布Kafka消息 │
│ │───────────────────>│
│ │ │
│ 7. 返回发布结果 │ │
│<───────────────────│ │
┌─────┴───────┐ ┌─────┴───────┐ ┌─────┴───────┐
│ 系统A │ │ IAP平台 │ │ Kafka │
└─────────────┘ └─────────────┘ └─────────────┘
描述:
申请Token :系统A调用 /interface/v1/token 接口获取Token
发布消息 :系统A调用 /interface/v1/publish/message 接口,提供topicName和消息内容
验证Token :IAP验证Token的有效性
验证发布权限 :IAP检查系统A是否有权发布该服务的消息
发布Kafka消息 :IAP将消息发布到 IAP_TOPIC_${servCode} 主题
返回结果 :IAP返回发布结果给系统A
步骤1:发布方(挂号系统)获取Token
-
接口 :
POST /iap/interface/token -
请求头:
javascriptsystemCode: 100 messageId: 1719225600100099903061234
步骤2:发布方发布消息
-
接口 :
POST /iap/interface/v1/publish/message -
请求头:
javascriptsystemCode: 100 messageId: 1719225600100099903075678 token: Token_100_abc123 -
请求体(加密后):
javascript{ "topicName": "new_appointment", "param": { "department": "内科", "date": "2023-07-25" } }
步骤3:订阅方处理消息
-
操作:订阅方(如互联网医院)从Kafka消费消息
"各订阅方,凭管理员提供的TOPIC和GROUP,订阅和消费消息。"
步骤4:订阅方确认消息
-
接口 :
POST /iap/interface/v1/commit/message -
请求头:
javascriptsystemCode: 101 messageId: 1719225600101099903089012 token: Token_101_def456 presessionid: session_from_kafka -
请求体(加密后):
javascript{ "publishMsgId": "消息ID_abc123", "ack": "1" // 1=成功,0=失败 }
✅ 场景3完整流程: 发布方拿Token → 发布方发布消息 → 订阅方消费消息 → 订阅方确认消息
注意 :此场景不调用
/iap/interface/v1/preauth(申请授权码)和/iap/interface/v1/authentication(认证授权)
五、常见错误与解决方案
| 错误码 | 错误信息 | 原因 | 解决方案 |
|---|---|---|---|
| 1000 | 请勿重新调用 | messageId重复 |
每次请求生成新24位ID |
| 1001 | 参数缺失 | 缺少必填参数 | 检查文档要求的参数是否都传了 |
| 1003 | 票根无效 | 用了错误的ticket | 重新调用/iap/interface/v1/preauth获取新ticket |
| 1004 | 票根重复消费 | 同一个ticket用了两次 | 每次跳转必须用新ticket |
| 1004 | 票根过期 | ticket超过100秒 | 生成后100秒内必须使用 |
| 1005 | 令牌无效 | Token过期或错误 | 重新调用/iap/interface/token |
| 1006 | 服务未授权 | 没订阅这个服务 | 先在管理平台订阅服务 |
| 1009 | 服务不存在 | 服务未上架 | 联系管理员上架服务 |
🚨 最高频错误:
messageId重复 (1000):集成平台不做幂等,必须每次生成新ID!
Token过期(1005):Token有效期通常30分钟,过期必须重申请!
票根过期 (1004):ticket100秒内必须用,否则重申请!
六、重要安全提醒(必须遵守!)
1. Token使用规则
-
Token是临时凭证,每次调用服务前都要验证是否有效
-
绝对不能把Token写死在代码里,每次用前检查有效期
-
Token泄露 = 身份被盗用,必须严格保密
2. 数据加密要求
-
所有业务参数必须SM4加密,不能传明文
-
加密示例(Java):
javascriptSM4 sm4 = SmUtil.sm4(HexUtil.decodeHex(secretKey)); String encrypted = sm4.encryptHex(plaintext);
3. 请求唯一性
-
messageId生成规则:-
0-12位: 13位时间戳 -
13-16位: 四位系统编码(不足左补0,如0100) -
17-20位: 接口目录编码(如0301) -
21-24位: 4位随机数 -
示例 :
1719225600100010003019876
-
4. 场景区分
-
页面跳转 → 用3.1+3.2接口
-
后台取数据 → 用3.4接口
-
群发通知 → 用3.5+3.7接口
七、快速自查清单(调用前必看)
✅ 通用检查:
-
请求头写了正确的
systemCode(100/101/901...) -
生成了24位唯一
messageId -
在加密的param中重复写入 相同的
messageId -
除Token接口外,其他接口都带了
token参数
✅ 场景1(页面跳转)检查:
-
A系统已调
/iap/interface/token -
A系统已调
/iap/interface/v1/preauth(带token!) -
重定向URL包含
ticket+timestamp+presessionid -
B系统已调
/iap/interface/v1/authentication验证
✅ 场景2(API调用)检查:
-
B系统已调
/iap/interface/token -
调用的是
/iap/runtime/v1/{uri},不是3.1/3.2接口 -
请求体包含
serviceCode和业务参数
✅ 场景3(消息广播)检查:
-
发布方已调
/iap/interface/token -
调用
/iap/interface/v1/publish/message时指定了topicName -
订阅方从Kafka消费后,已调
/iap/interface/v1/commit/message确认
八、总结
-
IAP集成平台通过统一的入口、严格的安全认证和高效的消息传递机制,实现了不同系统之间的安全、高效通信。
-
系统间的所有交互都通过IAP进行转发和验证,大大降低了系统间直接连接的复杂度和安全风险。
-
最后提醒:集成平台是工具,核心业务逻辑仍在各系统自身。
附录:关键接口速查表
| 场景 | 接口URL | 用途 | 必填参数 |
|---|---|---|---|
| 通用 | /iap/interface/token |
申请Token | clientId, clientSecret |
| 页面跳转 | /iap/interface/v1/preauth |
申请跳转凭证 | token, servCode |
| 页面跳转 | /iap/interface/v1/authentication |
验证跳转 | token, ticket, timestamp |
| API调用 | /iap/runtime/v1/{uri} |
调用服务 | token, serviceCode |
| 消息广播 | /iap/interface/v1/publish/message |
发布消息 | token, topicName |
| 消息广播 | /iap/interface/v1/commit/message |
确认消息 | token, publishMsgId, ack |