Skip to content

API 密钥

API 密钥是商户服务端签发访客身份凭证时使用的安全密钥。接入 Web 端时,网站服务器可以使用它对业务 ID、访客 ID、昵称等信息进行签名;系统校验签名后才会接受访客身份,从而避免仅凭浏览器传入的用户 ID 被伪造。

需要将第三方用户身份安全传递给客服系统时,请参阅用户基础信息对接

API 密钥只应保存在服务端,不能写入页面源码、浏览器脚本或前端代码仓库。

API 密钥设置

页面功能

项目说明
允许明文传输访客数据控制 Web 接口是否允许明文传输访客数据。修改开关后点击 保存
API 密钥当前业务用于签发访客身份令牌的密钥,长度为 32 位。
重新生成生成一组新密钥。新密钥保存后,使用旧密钥签发的令牌将无法继续使用。
保存保存明文传输设置或新生成的密钥。

重新生成密钥

  1. 点击 API 密钥右侧的重新生成图标。
  2. 在确认窗口中确认操作。
  3. 点击 保存,使新密钥生效。

重新生成密钥确认

重新生成密钥会立即淘汰旧密钥对应的鉴权链接。若已有网站正在使用鉴权,请先完成服务端切换,再保存新密钥。

Web 端鉴权方式

网页接入方式

网页端只配置 auth 回调,从您的业务服务端获取令牌。API 密钥和签名过程都放在服务端完成:

html
<script src="/static/js/kf.js"></script>
<script>
  window.KFVisitor.init({
    baseUrl: 'https://your-kefu-domain.example',
    businessId: 'YOUR_BUSINESS_ID',
    groupId: 0,
    auth: () => fetch('/api/visitor-token', {
      credentials: 'include'
    }).then((response) => {
      if (!response.ok) throw new Error('获取访客令牌失败');
      return response.text();
    })
  });
</script>

当访客发起对话时,接入代码会调用 auth(也支持返回 Promise),并将返回的令牌作为 token 参数提交到 Web 访客入口(/visitor?token=...)。您的 /api/visitor-token 接口应根据当前登录用户查询用户资料,在服务端签发令牌并直接返回令牌字符串;不要让浏览器自行拼接或计算签名。

令牌签名算法

Web 端令牌由三个部分组成:header.payload.signature,各部分均使用 Base64URL(去掉 = 编码。

  1. Header 固定为 {"alg":"HS256","typ":"JWT"}
  2. Payload 按以下顺序包含业务和访客信息:biduidnameavatarprofile_modeprofile_modeoverwritefill
  3. headerBase64 + "." + payloadBase64 使用 API 密钥进行 HMAC-SHA256,得到二进制签名。
  4. 将签名进行 Base64URL 编码,并按顺序拼接三个部分。

系统收到令牌后会用对应业务的 API 密钥重新计算签名;签名一致时,才采用令牌中的访客 ID、昵称和头像信息。

服务端示例

以下示例中的 apiKeybusinessId 和访客资料均应从服务端配置或业务数据库读取。

PHP

php
<?php

function base64url(string $value): string
{
    return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
}

function createVisitorToken(
    string $apiKey,
    string $businessId,
    string $uid,
    string $name = '',
    string $avatar = '',
    string $profileMode = 'overwrite'
): string {
    $header = base64url(json_encode([
        'alg' => 'HS256',
        'typ' => 'JWT',
    ], JSON_UNESCAPED_SLASHES));

    $payload = base64url(json_encode([
        'bid' => $businessId,
        'uid' => $uid,
        'name' => $name,
        'avatar' => $avatar,
        'profile_mode' => $profileMode,
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

    $signingInput = $header . '.' . $payload;
    $signature = base64url(hash_hmac('sha256', $signingInput, $apiKey, true));

    return $signingInput . '.' . $signature;
}

// /api/visitor-token:先验证当前登录用户,再返回以下结果
echo createVisitorToken($apiKey, $businessId, $user['id'], $user['name']);

Java

示例使用 Jackson 的 ObjectMapper 生成 JSON:

java
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;

static String createVisitorToken(
        String apiKey, String businessId, String uid, String name, String avatar
) throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();

    Map<String, String> header = new LinkedHashMap<>();
    header.put("alg", "HS256");
    header.put("typ", "JWT");

    Map<String, String> payload = new LinkedHashMap<>();
    payload.put("bid", businessId);
    payload.put("uid", uid);
    payload.put("name", name);
    payload.put("avatar", avatar);
    payload.put("profile_mode", "overwrite");

    String header64 = encoder.encodeToString(mapper.writeValueAsBytes(header));
    String payload64 = encoder.encodeToString(mapper.writeValueAsBytes(payload));
    String signingInput = header64 + "." + payload64;

    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
    String signature64 = encoder.encodeToString(
            mac.doFinal(signingInput.getBytes(StandardCharsets.UTF_8)));

    return signingInput + "." + signature64;
}

Go

go
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
)

type tokenHeader struct {
    Alg string `json:"alg"`
    Typ string `json:"typ"`
}

type tokenPayload struct {
    Bid         string `json:"bid"`
    Uid         string `json:"uid"`
    Name        string `json:"name"`
    Avatar      string `json:"avatar"`
    ProfileMode string `json:"profile_mode"`
}

func createVisitorToken(apiKey, businessID, uid, name, avatar string) (string, error) {
    encoder := base64.RawURLEncoding
    headerJSON, err := json.Marshal(tokenHeader{"HS256", "JWT"})
    if err != nil { return "", err }
    payloadJSON, err := json.Marshal(tokenPayload{
        businessID, uid, name, avatar, "overwrite",
    })
    if err != nil { return "", err }

    header64 := encoder.EncodeToString(headerJSON)
    payload64 := encoder.EncodeToString(payloadJSON)
    signingInput := header64 + "." + payload64

    mac := hmac.New(sha256.New, []byte(apiKey))
    mac.Write([]byte(signingInput))
    signature64 := encoder.EncodeToString(mac.Sum(nil))
    return signingInput + "." + signature64, nil
}

服务端接口返回令牌后,浏览器端的 auth 回调即可将它交给接入代码。这样第三方用户系统的登录状态与客服系统中的访客身份可以安全关联。