Skip to content

安全管家OpenAPI文档V1.0

1.概述

本文档目的是方便第三方应用系统对接安全管家,包括但不限于将事件接入到安全管家或者从安全管家订阅事件

2.前置条件

向安全管家(简称IOC工作台,IOC)申请应用AK,SK,用来鉴定请求合法性。

2.1 对接前准备工作

找交付经理申请应用的AK,SK: 提供第三方系统名称

数据接入:第三方系统在边端,和安全管家在同一个局域网内,可通过HTTP或RabbitMQ方式接入;如果第三方应用在云端,则需要通过云端RabbitMQ方式接入,需要提供云端RabbitMQ信息

数据订阅: 如需从安全管家订阅数据,需要提供HTTP/HTTPS推送地址或者RabbitMq信息(IP,Port,VirtualHost,User,Password)

数据订阅以HTTP/HTTPS订阅的,第三方系统需要对推送请求鉴权,鉴权机制请查阅 OpenAPI通用鉴权机制

2.2 OpenAPI通用鉴权机制

请求接口时,携带三个请求头:Authorization、Date、 Content-Type

Authorization: 格式为 appkey:sign ,将appKey和sign签名用冒号连接, appkey为申请应用ak,sign为通过下文算法计算出来,详见sign签名算法

Date: 为请求时的时间,格式为:EEE, dd MMM yyyy HH:mm:ss zzz,如 Wed, 19 Nov 2025 02:41:20 GM

Content-Type:固定为 application/json

注:对于Content-Type的值,在请求头中传输的值可能后面会多一个" ;charset=utf-8"。第三方服务最好将Content-Type的值做截取操作,只取";"前的字符串,避免校验不通过。

2.2.1 sign签名算法

  1. 拼接字符串:将HTTP方法、Content-Type(固定为 application/json)、日期字符串和路径资源按以下格式拼接:

    HTTP方法\nContent-Type\n日期字符串\n路径资源

  2. HmacSHA1加密:使用 appSecret 对拼接后的字符串进行HmacSHA1加密.

  3. Base64编码:对加密结果进行Base64编码。

  4. 截取签名:从Base64编码结果中截取第5位开始的10个字符作为签名。

鉴权逻辑Java代码参考:

详见附录 附录三:鉴权相关参考代码

请求接口Java代码参考:

详见附录 附录三:鉴权相关参考代码


3.名称解释

事件:系统或服务中发生的状态变化,在变化超过阈值时,会主动出发通知, 要求立刻处理。本文中的事件也可被称为告警, 二者等同。


4.系统交互图介绍

安全管家会在事件状态发生变化时推送事件到订阅的第三方系统。状态的变化包括事件产生、事件由待办转为处理中、事件关闭。


5.接口文档

5.1 查询事件类型列表

接口定义和请求方式

接口地址: /api/open/queryIncidentTypeList

请求方式: POST

接口请求参数介绍:

字段名称数据类型备注
priority事件等级Integer非必填项,为空则查询全部

0:低风险 1:中风险
2:高风险 3:极高风险
pageSize页大小Integer非必填项,
为空则使用默认值10
pageNum页码Integer非必填项,
为空则使用默认值1
scene场景Integer非必填项,为空则查询全部

1-报事事件
2-设备事件
3-AI事件
4-业务事件
5-消防事件

接口返回值介绍:

字段名称数据类型备注
code状态码String200:成功
success是否成功Booleantrue:成功; false:失败;
data承载数据Object
data中包含的对象信息
pageNum当前页码Integer由入参决定
pageSize当前页码数据量Integer由入参决定
total数据量总数Integer事件的数据总量
id事件类型idString字符串长度为64个字符以下
scene场景编码Integer1-报事事件, 2-设备事件, 3-AI事件, 4-业务事件, 5-消防事件
sceneStr场景String场景编码对应的场景名称
originParentCode原始父编码String注:字符串长度为128个字符以下
originCode原始编码String注:字符串长度为128个字符以下
parentName父名称String注:字符串长度为64个字符以下
name名称String注:字符串长度为64个字符以下
code唯一标识String注:字符串长度为128个字符以下
status是否启用Integer1-是 0-否
deleted是否删除Integer0-否,1-是
priority事件等级标识Integer0:低风险
1:中风险
2:高风险
3:极高风险
priorityStr事件等级String事件等级标识对应的名称
createTime创建时间String使用"yyyy-MM-dd HH:mm:ss"格式
updateTime更新时间String使用"yyyy-MM-dd HH:mm:ss"格式
typeOriginCode一级事件类型codeString注:字符串长度为64个字符以下
typeParentName一级事件类型nameString注:字符串长度为64个字符以下

请求示例:

请求参数:

(空)

响应报文:

json
{
  "success": true,
  "code": "200",
  "data": {
    "records": [
      {
        "id": "-7661345827197248065",
        "scene": 3,
        "sceneStr": "AI事件",
        "originParentCode": "AISB_RYSD",
        "originCode": "AISB_RYSD",
        "parentName": "AI识别-人员摔倒",
        "name": "AI识别-人员摔倒",
        "code": "AISB_RYSD",
        "status": 0,
        "deleted": 0,
        "priority": 2,
        "priorityStr": "高风险",
        "createTime": "2025-11-18 14:36:02",
        "updateTime": "2025-11-18 15:23:09",
        "typeOriginCode": "AISB_RYSD",
        "typeParentName": "AI识别-人员摔倒"
      }
    ],
    "total": 79,
    "pageSize": 10,
    "pageNum": 1
  }
}

5.2 查询事件类型详情

接口定义和请求方式

接口地址: /api/open/queryIncidentTypeDetail

请求方式: POST

接口请求参数介绍

字段名称数据类型备注
secondTypeCode二级事件编码String必填

接口返回值介绍:

字段名称数据类型备注
code状态码String200:成功
success是否成功Booleantrue:成功; false:失败
data承载数据Object事件类型详情的查询结果
data中包含的对象信息
id事件类型idString事件类型的id
scene场景编码Integer1-报事事件, 2-设备事件,
3-AI事件, 4-业务事件,
5-消防事件
sceneStr场景String场景编码对应的场景名称
originParentCode原始父编码String注:字符串长度大概为64个字符以下
originCode原始编码String注:字符串长度大概为64个字符以下
parentName父名称String注:字符串长度大概为64个字符以下
name名称String注:字符串长度大概为64个字符以下
code唯一标识String注:字符串长度大概为64个字符以下
status是否启用Integer1-是 0-否
deleted是否删除Integer0-否,1-是
priority事件等级标识Integer0:低风险
1:中风险
2:高风险
3:极高风险
priorityStr事件等级String事件等级标识对应的等级名称
createTime创建时间String使用"yyyy-MM-dd HH:mm:ss"格式
updateTime更新时间String使用"yyyy-MM-dd HH:mm:ss"格式
voiceScene提示声音类型Integer事件在安全管家上报时,系统的提示音类型
typeOriginCode一级事件类型codeString注:字符串长度大概为64个字符以下
typeParentName一级事件类型nameString注:字符串长度大概为64个字符以下

返回报文:

json
{
  "isOk": true,
  "msg": null,
  "cause": null,
  "code": "200",
  "data": {
    "id": "-7661345827197248065",
    "scene": 3,
    "sceneStr": "AI事件",
    "originParentCode": "AISB_RYSD",
    "originCode": "AISB_RYSD",
    "parentName": "AI识别-人员摔倒",
    "name": "AI识别-人员摔倒",
    "code": "AISB_RYSD",
    "businessName": "AI识别-人员摔倒",
    "businessKey": "AISB_RYSD",
    "thresholdDuration": 0,
    "thresholdTimes": 0,
    "status": 0,
    "autoReport": 0,
    "autoReportStr": "否",
    "aiCheck": 1,
    "aiCheckStr": "是",
    "dispatchType": "workOrder",
    "autoDistributeTo": "workOrder",
    "deleted": 0,
    "priority": 2,
    "priorityStr": "重要",
    "createTime": "2025-11-18 14:36:02",
    "updateTime": "2025-11-18 15:23:09",
    "voiceScene": 1,
    "typeOriginCode": "AISB_RYSD",
    "typeParentName": "AI识别-人员摔倒",
    "isWindow": 0
  }
}

5.3 分页查询事件列表

接口定义和请求方式

接口地址: /api/open/queryIncidentList

请求方式:POST

接口请求参数介绍:

字段名称数据类型备注
pageNum页码Integer非必填项,不填默认为1
需要查询第几页
pageSize每页数据量Integer非必填项,不填默认为10
每页的数据量
code事件编号String非必填项
字符串长度大概为64个字符以下
typeId事件类型等级idList<String>非必填项
0-低风险,1-中风险
2-高风险,3极高风险
incidentType事件状态String非必填项
0:待处理 1:已结束 2:处理中
startTime创建时间(开始)String非必填项
定义一个时间作为事件结开始时间搜索的起点
格式:"yyyy-MM-dd HH:mm:ss"
endTime创建时间(结束)String非必填项
定义一个时间作为事件结开始时间搜索的终点
格式:"yyyy-MM-dd HH:mm:ss"
eventStartTime关闭时间(开始)String非必填项
定义一个时间作为事件结束时间搜索的起点
格式:"yyyy-MM-dd HH:mm:ss"
eventEndTime关闭时间(结束)String非必填项
定义一个时间作为事件结束时间搜索的终点
格式:"yyyy-MM-dd HH:mm:ss"

接口返回值介绍:

字段名称数据类型备注
code状态码String200为成功
success是否成功Booleantrue:成功;false:失败
data承载数据Object分页查询结果
data中包含的对象信息
pageNum当前页码Integer由入参决定
pageSize当前页码数据量Integer由入参决定
total数据量总数Integer事件的数据总量
records数据详情List<IncidentDto>事件列表
IncidentDto中包含的事件字段
code事件编号String字符串长度在64个字符字符以下
originIncidentCode第三方事件编号String事件源上报的事件编号,可追溯至相关系统产生的源事件
name事件名称String对应事件的名称
range项目String项目名称
position位置String报事设备的位置
typeKey事件类型String事件对应的二级事件编码
字符串长度在128个字符字符以下
typeName事件类型名称String事件对应的二级事件编码名称
字符串长度在64个字符字符以下
incidentType事件状态String"0"--待处理;
"1"--已结束;
"2"--处理中;
createTime创建时间String事件上报至安全管家的时间
eventEndTime关闭时间String事件的关闭时间
alarmDuration持续时间String事件的持续时间(已换算成X天X小时X分X秒)

请求示例:

请求参数:

json
{
  "id": "",
  "code": "",
  "typeId": [
    "1980462046801563649"
  ],
  "incidentType": 1,
  "queryType": 1,
  "pageNum": 1,
  "pageSize": 15,
  "startTime": "2025-12-03 00:00:00",
  "endTime": "2025-12-05 23:59:59",
  "eventStartTime": "",
  "eventEndTime": ""
}

响应报文:

json
{
  "success": true,
  "code": "200",
  "data": {
    "records": [
      {
        "id": "1995301301597569024",
        "code": "20251201AIYJ000095",
        "originIncidentCode": "747173778632773",
        "name": "AI识别-公共区域垃圾",
        "range": "测试项目",
        "position": "X座X单元XX区域",
        "typeName": "AI识别-公共区域垃圾",
        "typeKey": "AISB_GGQYLJ",
        "createTime": "2025-12-01 09:17:56",
        "eventEndTime": null,
        "alarmDuration": "",
        "incidentType": "0"
      },
      {
        "id": "1995301121229914112",
        "code": "20251201AIYJ000094",
        "name": "AI识别-公共区域垃圾",
        "range": "测试项目",
        "typeName": "AI识别-公共区域垃圾",
        "typeKey": "AISB_GGQYLJ",
        "createTime": "2025-12-01 09:17:13",
        "eventEndTime": null,
        "alarmDuration": "",
        "position": "X座X单元XX区域",
        "incidentType": "0",
        "originIncidentCode": "747173601284165"
      }
    ],
    "total": 7524,
    "pageSize": 15,
    "pageNum": 1,
    "pages": 502
  }
}

5.4 查询事件详情

接口定义和请求方式:

接口地址:/api/open/queryIncidentDetail

请求方式: POST

接口请求参数介绍:

字段名称数据类型备注
id事件idString必填项
事件的唯一id

请求示例

请求参数:

json
{
  "id": "1995297557426274304"
}

接口返回值介绍:

字段名称数据类型备注
code状态码String200为成功
success是否成功Booleantrue:成功;false:失败
data承载数据Object分页查询结果
incidentDetail事件详情信息Object包含在data中
存储事件的详情信息
logOperationList调度记录List<LogOperationDto>包含在data中
存储该事件相关的调度和操作记录,如处理流程的派单,关闭等操作。
records事件备注List<TempRecordModel>包含在data中
存储事件的一些备注信息。

incidentDetail包含字段

字段名称数据类型备注
id事件idString事件的唯一id
name事件名称String字符串长度大概为64个字符以下
description事件描述String对于事件的描述,通常为事件名称
source事件来源String事件由哪个系统上报到安全管家
deviceName报事设备名称String上报此事件的设备
position位置String报事设备位置
createTime上报时间String安全管家接收到事件的时间
pictureUrl图片String此处为一个Json数组转化的字符串, 具体见报文示例中pictureUrl的格式

LogOperationDto包含字段

字段名称数据类型备注
eventId事件编号String字符串长度大概为64个字符以下
workOrderNo工单编号String字符串长度大概为64个字符以下
eventStatus流程状态String事件调度流程状态码
eventName流程名称String事件调度流程状态文字描述
字符串长度大概为32个字符以下
eventClosed工单是否关闭Booleantrue: 关闭
false: 未关闭
eventHandler事件处理人String处理该事件的人员,如果为自动派单,该字段固定展示为:"AIOT告警自动派单"
eventHandlerMobile处理人手机号String处理人的手机号码,如果为自动派单,则此字段为null
eventTime处理时间String处理该事件的事件,格式为"yyyy-MM-dd HH:mm:ss"
eventType事件类型String此处为固定字符串"incident",表示为安全管家的事件。
source来源String固定为字符串"system",代表调度记录由安全管家系统生成
content节点描述String对于调度节点的描述信息
字符串长度不限。
online是否在线String0:不在线;
1:在线.
reasonOptionAI事件关闭-原因选项String两个选项:
风险已解除;
识别错误;
reasonDescriptionAI事件关闭-原因手动输入的描述String字符串长度为不超过256个字符
robotId机器人idString告警机器人或作业机器人的id
该字段只在派单类型为移动巡检时赋值
注:字符串长度为64个字符以下
robotCameraId机器人绑定摄像头idString机器人身上绑定摄像头的id
该字段只在派单类型为移动巡检时赋值
注:字符串长度为64个字符以下

TempRecordModel包含字段

字段名称数据类型备注
id事件idString事件的唯一id
content内容String事件备注的内容
字符串长度不限
operator操作人String撰写备注的人员
phone手机号String操作人的手机号码
createTime创建时间String备注的创建时间

响应报文:

json
{
  "success": true,
  "code": "200",
  "data": {
    "incidentDetail": {
      "id": "1992847879056371712",
      "code": "20251124AIYJ000004",
      "name": "散落垃圾",
      "typeId": "1980462046801563649",
      "typeName": "散落垃圾",
      "typeNameKey": "AIYJ_SLLJ",
      "parentKey": "AIYJ",
      "createTime": "2025-11-24 14:48:53",
      "eventEndTime": null,
      "alarmDuration": null,
      "source": "集成服务",
      "projectId": "P0000",
      "projectName": "测试项目",
      "startTime": "2025-11-24 14:48:53",
      "incidentTime": "2025-11-24 14:27:21",
      "position": "测试位置",
      "alarmStatus": null,
      "description": "散落垃圾",
      "pictureUrl": "[\"https://spfile-sit.icloudcity.com/spioc/202511/5f3a4c33a24632e4e844/1c33052e4d30d03b.jpg\"]",
      "deviceId": "7336649346697510912",
      "deviceName": "测试设备",
      "incidentType": "0",
      "incidentCreateTime": "2025-11-24 14:48",
      "mdmDeviceCode": "IOTPR03057100020",
      "mdmDeviceName": "测试设备",
      "originIncidentCode": "745153135341103",
      "originParentCode": "AIYJ_SLLJ",
      "dispatchType": "workOrder"
    },
    "logOperationList": [
      {
        "eventId": "1992847879056371712",
        "workOrderNo": null,
        "eventStatus": "00",
        "eventName": "创建事件",
        "eventClosed": false,
        "eventHandler": "AIOT告警自动派单",
        "eventTime": "2025-11-24 14:48",
        "eventType": "incident",
        "reasonOption": null,
        "reasonDescription": null,
        "robotId": "888620",
        "robotCameraId": "888620"
      }
    ],
    "records": []
  }
}

6.事件订阅

6.1 增量事件订阅

实时推送事件,事件状态变化,都进行事件推送。包括待处理,进行中,关闭三种状态。

支持RabbitMq方式推送,需要应用方提供RabbitMq配置信息。

RabbitMQ方式订阅:

RabbitMQ的配置规则参考 附录二:RabbitMQ配置相关规则

推送事件报文格式介绍:

字段名称数据类型备注
objectType订阅类型String1-事件; 2-事件类型.
此处固定为1
dataType数据类型String1-增量数据; 2-全量数据.
data数据详情List<Object>增量事件订阅时此处列表为1个元素,即安全管家新生成的事件
List中包含的对象信息
code事件编码String包含在Object中
注:字符串长度不超过64个字符
name事件名称String注:字符串长度不超过32个字符
originIncidentCode第三方事件编码String注:字符串长度不超过64个字符
projectCode项目编码String注:字符串长度不超过32个字符
projectName项目名称String注:字符串长度不超过32个字符
position位置String报事位置信息
pictureUrl事件图片String事件的图片url地址。
注:字符串长度不限,存储到数据库可使用LONGTEXT类型
typeKey事件类型(二级事件编码)String注:字符串长度不超过64个字符
typeName事件类型名称(二级事件名称)String注:字符串长度不超过32个字符
incidentType事件状态String0:待处理
1:已完成
2:处理中
createTime事件创建时间String事件推送到安全管家的时间
eventEndTime事件关闭时间String事件在安全管家被关闭的时间
alarmDuration事件持续时间String事件开始到结束共多久
已换算为X天X小时X分钟X秒
注意: 如果推送的事件不是已完成的状态, 此字段的值为空
alarmDurationLong事件持续时间Long事件开始到结束共多久
(单位: 秒)
注意: 如果推送的事件不是已完成的状态, 此字段的值为空
deviceId报事设备编码String注:字符串长度不超过64个字符
deviceName报事设备名称String注:字符串长度不超过32个字符
thirdCode第三方设备编码String注:字符串长度不超过64个字符
thirdName第三方设备名称String注:字符串长度不超过64个字符
workOrderCode工单编码String注:字符串长度不超过64个字符

推送报文示例:

json
{
  "objectType": "1",
  "dataType": "1",
  "data": [{
    "code": "20251106AIYJ000527",
    "originIncidentCode": "738093083545669",
    "name": "周界入侵",
    "projectCode": "P0000",
    "projectName": "测试项目",
    "position": "测试位置",
    "pictureUrl": "[\"https://spfile-uat.icloudcity.com/spioc/202511/18818ffed6116b378/1430a7687bbad2b98cd.jpg\"]",
    "typeKey": "AIYJ_XQZJRQ",
    "typeName": "周界入侵",
    "incidentType": "1",
    "createTime": "2025-11-06 10:11:00",
    "eventEndTime": "2025-11-06 10:13:16",
    "alarmDuration": "0天00小时2分钟16秒",
    "alarmDurationLong": 136,
    "deviceId": "IOTPR00001124200",
    "deviceName": "A-TEST-ANGG-2001",
    "thirdCode": "7321429522836889600",
    "thirdName": "A-TEST-ANGG-2001"
  }]
}

7.附录

7.1 附录一:二级事件类型编码和名称对应关系

AI事件类型以及编码

二级事件类型名称二级事件类型编码
周界入侵AIYJ_XQZJRQ
机动车违停AIYJ_JDCWT
车辆拥堵AIYJ_JDCDS
消防通道占用AIYJ_XFTDDFZW
人员拥堵AIYJ_CRKYD
垃圾桶满溢AIYJ_LJTMY
桶边垃圾AIYJ_TBLJ
公共区域垃圾AIYJ_GGQYLJ
员工离岗AIYJ_YGLG
人员翻越AIYJ_RYFY
室内明火检测AIYJ_SNMHJC
烟雾检测AIYJ_YWJC
专用车位占用AIYJ_ZYCWZY
消防设备异常AIYJ_MHQQS
抽烟识别AIYJ_CYSB
人员摔倒AIYJ_RYSD
人员逗留AIYJ_RYDL
非机动车违停AIYJ_FJDCWT
机动车滞留AIYJ_JDCZL
玩手机识别AIYJ_WSJSB
横幅识别AIYJ_HFSB
散落垃圾AIYJ_SLLJ

7.2 附录二:RabbitMQ配置相关规则

事件订阅和事件类型订阅

RabbitMQ配置规则建议:

exchange类型:Topic类型

exchange命名规则:ioc_

routingkey命名规则:ioc_incident_out.$

queue命名规则建议:ioc_incident_queue_out_${appId}.${projectCode}

注:

对于queue的命名可根据实际情况是否需要拼接项目编码,由第三方系统自己创建,并绑定exchange。

7.3 附录三:鉴权相关参考代码

Java代码参考:

鉴权代码:

java
package com.onewo.openapi.auth;

import com.onewo.spsms.management.util.OkHttpUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * @author yangming
 */
@Slf4j
public class OpenApiClientAuth {

    protected OpenApiClientAuth() {
    }

    public static final String AUTHORIZATION = "Authorization";
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String DATE = "Date";
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter
            .ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);

    /**
     * @param httpMethod 请求方式 GET,POST,PUT。。。
     * @param url http://baidu.com/xxx/xx?aa=11
     * @param appKey 123123
     * @param appSecret sdfdsf
     * @param contentType 请求类型
     * @return 返回授权说需要的Header参数
     */
    @SneakyThrows
    public static Map<String, String> getSignHeaders(final String httpMethod, final String url,
                                                     final String appKey, final String appSecret,
                                                     final String contentType) {
        final String pathResource = OpenApiClientAuth.getPathResource(url);
        final String contentTypeValue = contentType != null ? contentType : "application/json";
        final String dateValue = DATE_TIME_FORMATTER.format(ZonedDateTime.now(ZoneId.of("GMT")));
        final String sign = OpenApiClientAuth
                .calculateSing(httpMethod, contentTypeValue, dateValue, pathResource,
                        appSecret);
        final String authorizationString = appKey + ":" + sign;
        final Map<String, String> authHeaders = new HashMap<>(4);
        authHeaders.put(OpenApiClientAuth.DATE, dateValue);
        authHeaders.put(OpenApiClientAuth.AUTHORIZATION, authorizationString);
        authHeaders.put(OpenApiClientAuth.CONTENT_TYPE, contentTypeValue);
        return authHeaders;
    }

    protected static String getPathResource(final String url) {
        final String substring = url.substring(url.indexOf("://") + 3);
        return substring.substring(substring.indexOf("/"));
    }

    protected static String calculateSing(final String httpMethodString,
                                          final String contentTypeValue, final String dateValue, final String pathResource,
                                          final String appSecret) throws InvalidKeyException, NoSuchAlgorithmException {
        OpenApiClientAuth.log.debug("method = {}", httpMethodString);
        OpenApiClientAuth.log.debug("contentTypeValue = {}", contentTypeValue);
        OpenApiClientAuth.log.debug("dateValue = {}", dateValue);
        OpenApiClientAuth.log.debug("pathResource = {}", pathResource);
        OpenApiClientAuth.log.debug("appSecret = {}", appSecret);
        final String signToString = OpenApiClientAuth
                .builderStringToSign(httpMethodString, contentTypeValue, dateValue,
                        pathResource);
        OpenApiClientAuth.log.debug("signToString = {}", signToString);
        final String base64HashString = HMAC.hmacSha1Encrypt(signToString, appSecret);
        OpenApiClientAuth.log.debug("base64HashString = {}", base64HashString);
        final String sign = base64HashString.substring(5, 15);
        OpenApiClientAuth.log.debug("sign = {}", sign);
        return sign;
    }

    protected static String builderStringToSign(final String method,
                                                final String contentTypeValue, final String dateValue, final String pathResource) {
        return method
                + "\n"
                + OpenApiClientAuth.handleNullString(contentTypeValue)
                + "\n"
                + OpenApiClientAuth.handleNullString(dateValue)
                + "\n"
                + pathResource;
    }

    protected static String handleNullString(final String str) {
        return !StringUtils.hasText(str) ? "" : str;
    }

    protected static class HMAC {
        private HMAC() {
        }

        private static final String KEY_MAC_SHA1 = "HmacSHA1";

        public static String hmacSha1Encrypt(final String encryptText, final String encryptKey)
                throws NoSuchAlgorithmException, InvalidKeyException {
            final byte[] text = encryptText.getBytes(StandardCharsets.UTF_8);
            final byte[] keyData = encryptKey.getBytes(StandardCharsets.UTF_8);
            final SecretKeySpec secretKey = new SecretKeySpec(keyData, HMAC.KEY_MAC_SHA1);
            final Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            return new String(Base64.getEncoder().encode(mac.doFinal(text)),
                    StandardCharsets.UTF_8);
        }
    }

    /**
     * get请求
     */
    public static String openApiClientGet(String url, String body, String clientId, String clientSecret) {
        Map<String, String> signHeaders = getSignHeaders(HttpMethod.GET.toString(), url, clientId, clientSecret, MediaType.APPLICATION_JSON_VALUE);
        return OkHttpUtil.get(url, signHeaders, String.class);
    }

    /**
     * post请求
     */
    public static String openApiClientPost(String url, String body, String clientId, String clientSecret) {
        Map<String, String> signHeaders = getSignHeaders(HttpMethod.POST.toString(), url, clientId, clientSecret, MediaType.APPLICATION_JSON_VALUE);
        return OkHttpUtil.post(url, body, signHeaders, String.class);
    }
}

请求调用接口时鉴权Java代码参考:

java
public static void main(String[] args) {
    String getUrl = "http://替换为实际的IP:30692/api/open/testGet?queryParam=GET参数";
    String postUrl = "http://替换为实际的IP:30692/api/open/testPost";
    String appkey = "替换为实际的appKey";
    String appSecret = "替换为实际的appSecret";
    // 无body示例
    String result1 = OpenApiClientAuth.openApiClientGet(getUrl, null, appkey, appSecret);
    log.info("OpenApi-test GET:{}",result1);
    String result2 = OpenApiClientAuth.openApiClientPost(postUrl, "{\"name\":\"POST参数\"}", appkey, appSecret);
    log.info("OpenApi-test POST:{}",result2);
}