第三方对接通用设计方案
胡乐秋 2020-05-11 16:56:12 new new

第三方对接通用设计方案

目的

基于业主集成需求,经常委托一家单位完成多家第三方单位的信息集成。本设计文档可作为一个通用设计方案,实现信息系统的集成对接。

集成方案

  1. 集成方提供显示区域: 一个浏览器(或iframe)窗口;
  2. 被集成方提供显示内容: 一个网页页面;
  3. 双方按照"url请求接口"予以对接。

这样可解耦集成方与被集成方, 实现双方各自的最大化扩展和最小化依赖。

需要解决两个问题

  1. 集成方将用户信息和访问页面url传递给被集成方
  2. 被集成方验证本次访问请求的安全(合法性以及不被冒充)

url请求接口

  1. url地址: http://_1_._2_._3_:_4_/third_visit?third_party_account=_5_&nonce=_6_&signature=_7_&user_account=_8_&next_url=_9_
  2. 编码: UTF-8
  3. 方法: GET
  4. 调用方: 集成方
  5. 实现方: 被集成方
  6. 请求参数
_1_._2_._3_:_4_ 服务器地址和端口,被集成方提供,线下交换
_5_: 集成方在被集成系统里的账户, 被集成方提供,线下交换
_6_: 本次访问随机数
_7_: 本次签名
_8_: 当前用户帐户
_9_: url实际请求页面地址
  1. 返回"next_url"参数(默认为"/")指示的页面或"访问违例"的错误信息页面

安全

每一个url请求都必须携带特定的安全信息,以便于双方进行授权验证。
安全信息参数包括:

url请求参数

  1. third_party_account: 集成方在被集成系统里的账户, 被集成方提供,线下交换,url显示携带
  2. nonce: 本次访问二位整数随机数, 集成方随机生成, url显示携带
  3. signature: 本次签名, 集成方计算生成, url显示携带
  4. user_account: 当前用户帐户, 集成方提供, url显示携带
  5. next_url: url实际请求页面地址, 集成方提供, url显示携带

线下交换参数

  1. token: 集成方在被集成系统里的访问令牌(密码), 被集成方提供,线下交换, url不携带
  2. timestamp: 本次访问日期,双方取自本地操作系统,url不携带(格式YYYYMMDD: 20170131)

验证流程

  1. 从集成方url请求中提取"nonce"、"signature"、"third_party_account"三个参数;
  2. 根据"third_party_account"查询线下交换的集成方访问令牌"token";
  3. 从本地系统获取当前日期时间戳"timestamp"参数(年月日: 20200229);
  4. 将"token", "timestamp", "nonce"三个参数正向排序并连接为字符串"target_string";
  5. 将"target_string"进行sha1加密并输出为十六进制字符串"target_signature";
  6. 将生成的"target_signature"与url携带的"signature"签名进行比较,相同则表示该请求为合法的第三方调用。
  7. 如果签名验证成功,返回url请求携带的"next_url"参数(默认为"/")指示的页面,否则显示"访问违例"的错误信息页面

伪码实现

third_party_account = request.query.third_party_account         # 从url获取
nonce = request.query.nonce                                     # 从url获取
signature = request.query.signature                             # 从url获取
token = get_token_by_third_party_account(third_party_account)   # 根据集成方账户得到访问令牌(密码),线下交换
timestamp = datetime.datetime.now().strftime('%Y%m%d')          # 从本机获取(年月日: 20200229)
target_string = ''.join(sorted([token, timestamp, nonce]))      # 排序
expect_signature = hashlib.sha1(target_string.encode()).hexdigest() # sha1计算签名
valid_signature = (expect_signature == signature)               # 比较签名

计算实例

# 声明变量
third_party_account= "shtw"
nonce="04"
signature="1234"
token="shtw_experiment_2019"
timestamp="20190131"
# 计算签名
target_string = "0420190131shtw_experiment_2019"
expect_signature = "6ab540e713a6a06d9769903b774c35dff897e970"  # 期望签名
valid_signature = (expect_signature == signature)  # url携带签名是否等于期望签名
assertTrue(valid_signature)