七鱼开发文档中心
总览服务端 APIWebiOSAndroid鸿蒙Uniapp微信小程序插件三方接入

通用说明

服务端 API 接入、签名校验方式和通用错误码。

服务端 API 面向后端系统对接,用于把企业自有系统与七鱼、云商知识库等能力连接起来。开始开发前,先确认接口的调用方向和业务分组,再按对应场景准备凭证、签名或 token。

由于调用场景不同,以及对于安全性要求的提高,七鱼系统目前存在以下几种常见签名校验方式。

调用方向校验方式适用场景凭证/签名位置
企业服务器 -> 七鱼服务器七鱼签名员工管理、客户管理、工单、短信、企微等通用七鱼 OpenAPIappKey、time、checksum 和可选 checksumAlgorithm 放在 query 中
企业服务器 -> 云商服务器云商签名知识库X-YS-APIKEY、X-YS-TIME、X-YS-SIGNATURE 放在请求头中
七鱼服务器或七鱼工作台 -> 企业服务器校验 token企业提供用户信息、订单信息等对接接口token 随七鱼请求传给企业接口
七鱼服务器 -> 企业服务器校验签名消息事件、CRM 认证、商品订单查询、工单回调、机器人动态内容等企业提供接口通常为 time、nonce、checksum/checkSum 等请求参数或请求头

如果接口页面未特别说明使用云商签名,默认按七鱼签名接入。企业提供接口给七鱼调用时,请优先阅读对应业务文档和 企业接口接入,因为这类接口的调用方向和签名字段与企业调用七鱼 OpenAPI 不完全相同。

七鱼签名

服务端 API 中的通用七鱼 OpenAPI 使用七鱼签名。调用方需要在 query 中传入 appKey、time 和 checksum,部分接口还支持传入 checksumAlgorithm 指定最终哈希算法。七鱼服务端使用 checksum 校验调用方身份和请求完整性。

七鱼签名和七鱼调用企业接口时的签名容易混淆:

场景调用方向参与计算字段
企业调用七鱼 OpenAPI企业服务器 -> 七鱼服务器appSecret + md5 + time,其中 md5 为请求体或文件内容的 MD5
七鱼调用企业接口七鱼服务器 -> 企业服务器常见为 appSecret + nonce + time,其中 nonce 为七鱼生成的随机字符串

两者不要混用。企业调用七鱼 OpenAPI 时,以本文档的 数据校验 为准;企业提供接口给七鱼调用时,以 校验签名、企业接口接入 和具体业务文档为准。

数据校验

企业服务器调用七鱼 OpenAPI 时,POST 请求需要在 query 中带以下参数:

参数参数类型参数说明是否必填
appKeyString企业的 appKey,可在七鱼管理后台查看是
timeLong当前 UTC 时间戳,从 1970 年 1 月 1 日 0 点 0 分 0 秒开始到现在的秒数是
checksumString默认按 SHA1(appSecret + md5 + time) 计算;当接口声明并传入 checksumAlgorithm=1 时,按 SHA-256(appSecret + md5 + time) 计算;结果转化成 16 进制字符(String,小写)是
checksumAlgorithmInteger可选签名算法参数;仅接口参数表中声明该字段时需要展示和传入。0 或不传表示 SHA1,1 表示 SHA-256。否

其中,checksum 用于校验数据的完整性,其计算规则中,各字段取值如下:

  • appSecret 可在七鱼管理后台 ->「系统」->「扩展与集成」页面找到。
  • md5 为整个请求 JSON 字符串的 md5 哈希值(小写形式),即 md5 = MD5(content).toLowerCase()。如果是上传文件,则是整个文件内容的 md5。
  • time 就是请求参数中的 time。

特殊说明:出于安全性考虑,每个 checksum 的有效期为 5 分钟,请确认发起请求的服务器是与标准时间同步的,比如有 NTP 服务。

计算公式:

checksum = SHA1(appSecret + md5 + time)
md5 = MD5(请求体原文).toLowerCase()
time = 秒级时间戳

如果接口声明并传入 checksumAlgorithm=1:
checksum = SHA-256(appSecret + md5 + time)

API 调试窗口

每个 API 文档页右侧的调试窗口已内置七鱼签名辅助能力。开发者在调试窗口中填写 appKey 和 appSecret 后,点击「调试」时,窗口会自动写入当前秒级 time,并按请求体原文计算 md5 和 checksum,无需手动填写这两个字段。

如果接口参数中存在 checksumAlgorithm,调试窗口会读取本次请求中的该参数值:传 1 时使用 SHA-256 生成 checksum;未传、传 0 或其他值时使用默认 SHA1 生成 checksum。如果接口参数表中没有 checksumAlgorithm,调试窗口不会展示该字段,计算签名时按 0 处理。checksumAlgorithm 本身需要在参数区按接口要求填写。

以下为参考代码。

checksum 的 Java 示例代码

public class QiyuPushCheckSum {

    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public static String encode(String appSecret, String md5, long time) {
        return encode(appSecret, md5, time, 0);
    }

    public static String encode(String appSecret, String md5, long time, int checksumAlgorithm) {
        String content = appSecret + md5 + time;
        String algorithm = checksumAlgorithm == 1 ? "SHA-256" : "SHA-1";
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
            messageDigest.update(content.getBytes());
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String getFormattedText(byte[] bytes) {
        int len = bytes.length;
        StringBuilder buf = new StringBuilder(len * 2);
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
        }
        return buf.toString();
    }
}

MD5 计算 Java 代码示例

public class MD5 {
    private final static char[] hexDigits = { '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    public static String md5(String input) {
        return md5(input, 32);
    }

    public static String md5(String input, int bit) {
        try {
            MessageDigest md = MessageDigest.getInstance(System.getProperty(
                    "MD5.algorithm", "MD5"));
            if (bit == 16)
                return bytesToHex(md.digest(input.getBytes("utf-8")))
                        .substring(8, 24);
            if (bit == 28) {
                return bytesToHex(md.digest(input.getBytes("utf-8")))
                        .substring(2, 30);
            }
            return bytesToHex(md.digest(input.getBytes("utf-8")));
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        int t;
        for (int i = 0; i < 16; i++) {
            t = bytes[i];
            if (t < 0)
                t += 256;
            sb.append(hexDigits[(t >>> 4)]);
            sb.append(hexDigits[(t % 16)]);
        }
        return sb.toString();
    }
}

请求七鱼 OpenAPI 接口 Java 代码示例

public static void main(String[] args) {
    // 请求内容,用来放到 body 体中
    String content = "{\"name\":\"qiyu\",\"age\":\"8\"}";
    // 对 content 进行 md5 哈希,然后转化为小写形式
    String md5 = MD5.md5(content).toLowerCase();
    // 取时间戳,注意是秒,不是毫秒
    long time = System.currentTimeMillis() / 1000;
    String appkey = "appkey";
    String secret = "secret";
    // 生成 checksum
    String checksum = QiyuPushCheckSum.encode(secret, md5, time);
    // 拼接请求 url
    String url = "https://qiyukf.com/xxx";
    url += "?appKey=" + appkey;
    url += "&time=" + time;
    url += "&checksum=" + checksum;
    // 发送 POST 请求,content 放到 body 体中
    String res = HttpClientPool.getInstance().postMethod(url, content);
}

重要提示: 本文档中提供的所有接口均面向开发者服务器端调用。用于计算 checksum 的 AppSecret 开发者应妥善保管,可在应用的服务器端存储和使用,但不应存储或传递到客户端,也不应在网页等前端代码中嵌入。

云商签名

知识库 使用云商签名,不使用七鱼签名。调用方需要在请求头中传入云商凭证和签名。

请求头必填说明
X-YS-APIKEY是云商 appKey。
X-YS-TIME是时间戳字符串,按原始字符串参与签名。
X-YS-SIGNATURE是使用 appSecret 计算的 HmacSHA256 签名。

签名原文:

needSign = timestamp + requestQuery + body
  • timestamp:请求头 X-YS-TIME 的原始值。
  • requestQuery:URL 中的 query string,不含 ?;没有 query 时为空字符串。
  • body:原始请求体字符串;无请求体时为空字符串。

签名算法:

signature = HexLower(HmacSHA256(needSign, appSecret))

知识库中 /api/athena/openApi/external/** 使用企业级云商凭证;/api/athena/agent/tool/** 使用个人 API 凭证,部分场景兼容企业级云商凭证。

云商签名 Java 示例代码

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class YsOpenApiSignature {

    public static String sign(String appSecret, String timestamp, String requestQuery, String body) {
        String needSign = valueOf(timestamp) + valueOf(requestQuery) + valueOf(body);
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            return toHexLower(mac.doFinal(needSign.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static String valueOf(String value) {
        return value == null ? "" : value;
    }

    private static String toHexLower(byte[] bytes) {
        StringBuilder builder = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            builder.append(String.format("%02x", b & 0xff));
        }
        return builder.toString();
    }

    public static void main(String[] args) {
        String appKey = "your-app-key";
        String appSecret = "your-app-secret";
        String timestamp = String.valueOf(System.currentTimeMillis());
        String requestQuery = "spaceId=123&page=1";
        String body = "{\"name\":\"example\"}";

        String signature = sign(appSecret, timestamp, requestQuery, body);

        // 请求时设置:
        // X-YS-APIKEY: appKey
        // X-YS-TIME: timestamp
        // X-YS-SIGNATURE: signature
    }
}

校验 token

校验 token 用于部分企业提供接口、七鱼调用企业接口的场景。企业在七鱼后台配置自定义 AppID、AppSecret 以及获取 token 的接口,七鱼调用企业接口时会携带 token,企业收到请求后可以校验 token 的正确性和时效性。

配置入口如下:

目前使用 token 方式校验的主要是以下企业接口:

接口调用说明
用户信息(在线)URL在线系统查询企业侧用户信息
用户信息(呼叫)URL呼叫系统查询企业侧用户信息
用户订单接口(在线)URL在线系统查询企业侧订单信息
用户订单接口(呼叫)URL呼叫系统查询企业侧订单信息

token 由七鱼后端调用企业提供的 token 接口获取,并根据返回时间进行缓存。前端触发上述能力时,会先通过七鱼封装的后端接口获取 token,再调用企业业务接口获取用户信息或订单信息。

注意:当获取 token 接口异常或超时时,七鱼会直接使用 AppSecret 作为 token 调用企业业务接口。企业实现校验 token 时,需要考虑这个兜底行为。

校验签名

校验签名用于七鱼调用企业提供 HTTP 接口的场景。企业在七鱼后台配置接口地址和 AppSecret,七鱼发起请求时携带时间戳、随机串和签名,企业侧用同一份 AppSecret 重新计算签名并比对。

最常见的签名规则如下:

checkSum = SHA1(appSecret + nonce + time)
nonce = 七鱼生成的随机字符串
time = 秒级时间戳

其中 appSecret 通常是企业在七鱼后台对应接入模块中配置的 AppSecret,nonce 不是请求体 MD5。不同业务可能会把 time、nonce、checksum/checkSum 放在 query 或 header 中,字段大小写也可能不同,请以具体业务文档为准。

常见请求字段

参数常见位置说明
appId / appidHeader 或 Body企业分配给七鱼的 AppID。部分接口不传或使用小写字段名。
timeHeader 或 Query当前 UTC 时间戳,通常精确到秒。
nonceHeader 或 Query七鱼生成的随机字符串,常见上限为 128 个字符。
checksum / checkSumHeader 或 Query七鱼根据约定规则计算出的签名。字段大小写以具体接口文档为准。
tokenHeader、Query 或 Body如果该业务同时启用了校验 token,七鱼会把获取到的 token 一并带给企业接口。

企业侧校验步骤

  1. 从请求中读取 time、nonce 和 checksum/checkSum。
  2. 使用企业在七鱼后台对应接入模块中配置的 AppSecret,按业务文档要求重新计算签名。
  3. 将重新计算出的签名与七鱼请求中携带的签名做字符串比较。
  4. 校验通过后再处理业务参数;校验失败时按业务文档约定返回错误响应。

建议企业侧同时检查 time 与当前服务器时间的偏差,避免历史请求被重放。允许的时间窗口可按业务安全要求设置,并确保服务器时间与标准时间同步。

校验签名 Java 示例代码

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

public class QiyuInboundCheckSum {

    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public static String encode(String appSecret, String nonce, String time) {
        String content = appSecret + nonce + time;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("sha1");
            messageDigest.update(content.getBytes(StandardCharsets.UTF_8));
            return getFormattedText(messageDigest.digest());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean verify(String appSecret, String nonce, String time, String checkSum) {
        String expected = encode(appSecret, nonce, time);
        return MessageDigest.isEqual(
                expected.getBytes(StandardCharsets.UTF_8),
                valueOf(checkSum).getBytes(StandardCharsets.UTF_8));
    }

    private static String valueOf(String value) {
        return value == null ? "" : value;
    }

    private static String getFormattedText(byte[] bytes) {
        StringBuilder buf = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            buf.append(HEX_DIGITS[(b >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[b & 0x0f]);
        }
        return buf.toString();
    }
}

常见场景差异

场景参数位置签名说明
商品和订单查询、机器人服务先知、一触即达通常在 Header 中携带 appid/appId、time、nonce、checksum/checkSum常见公式为 SHA1(appSecret + nonce + time)。
CRM 用户认证接口Header 携带 time、checksum,Body 为认证参数部分文档会把 nonce 定义为 MD5(body).toLowerCase(),此时按 SHA1(appSecret + nonce + time) 计算。
在线实时消息 URL 验证通常在 Query 中携带 time、nonce、echostr、checksum该场景会把加密消息体或解密后的字符串纳入验证和回显,必须按对应页面说明实现。
事件推送、工单回调等业务事件Query 或 Header,具体以业务页为准字段名、签名字段和失败响应结构可能随业务不同而不同。

如果具体业务页面给出了不同的参数位置、字段大小写或签名原文,应以具体业务页面为准。

通用错误码列表

错误码含义
200OK
14001参数 appKey 错误
14002参数 checksum 错误,请重新检查 checksum 计算方式
14003参数 time 错误,请确保 time 参数与当前时间差小于 5 分钟,或检查服务的时间是否与标准时间同步
14004内容格式校验错误,请参考详细错误信息,并对照对应的接口文档修正
14008请求来源 IP 不在被允许的范围内
14009请求频率过快,超出接口限制
14500服务器内部错误,请反馈给我们具体场景
14501数据量过大,超出接口限制
14515权限不足

备注:如果 Java 应用调用七鱼服务端接口出现证书报错问题,请升级 JDK 版本至 1.8_202 及以上。

这篇文档有帮助吗?

目录