Skip to content

用户基础信息对接

用户基础信息对接用于把业务系统中的用户身份传递给客服系统。支持传递用户 ID、昵称和头像,客服接待时可以直接识别访客身份。

两种信息传递方式

方式说明适用场景
普通传递网页直接把当前用户的编号、昵称和头像传给客服系统,配置简单。功能测试或不需要核验用户身份的页面。
安全传递网站服务器先核对用户登录状态,再生成身份凭证交给网页使用。正式业务接入,防止用户编号等身份信息被冒用或篡改。

正式网站建议使用安全传递方式。用于生成身份凭证的安全密钥只能保存在网站服务器中,不能写入网页代码。详细的配置方式请参阅 API 密钥

普通传递

普通传递直接在接入代码中填写用户基础信息:

html
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>在线咨询</title>
</head>
<body>
  <script src="https://live.99kf.com/static/js/kf.js"></script>
  <script>
    window.kfVisitor = KFVisitor.init({
      businessId: 'afxGIDLM',
      groupId: 0,
      baseUrl: 'https://live.99kf.com',
      displayMode: 'floating',
      visitor: {
        uid: 'user-10001',
        nickname: '张三',
        avatar: 'https://www.99kf.com/avatar/12.jpg'
      }
    });
  </script>
</body>
</html>

使用普通传递前,在 客服端 → 菜单 → 功能设置 中开启“允许明文传输访客数据”,并保存设置。由于信息直接来自网页,客服系统无法确认这些资料是否经过用户身份验证。

安全传递

安全传递时,网页不直接填写用户编号,而是向网站服务器请求身份凭证:

html
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>在线咨询</title>
</head>
<body>
  <script src="https://live.99kf.com/static/js/kf.js"></script>
  <script>
    window.kfVisitor = KFVisitor.init({
      businessId: 'afxGIDLM',
      groupId: 0,
      baseUrl: 'https://live.99kf.com',
      displayMode: 'floating',
      auth: () => fetch('/api/visitor-token', {
        credentials: 'include'
      }).then((response) => {
        if (!response.ok) throw new Error('获取访客令牌失败');
        return response.text();
      })
    });
  </script>
</body>
</html>

/api/visitor-token 由网站服务器实现:先根据当前登录状态取得用户资料,再使用安全密钥生成身份凭证并返回。网页将凭证交给客服系统完成身份校验,安全密钥始终留在服务器中。

鉴权算法

令牌由 header.payload.signature 三部分组成:

  1. Header 固定为 {"alg":"HS256","typ":"JWT"}
  2. Payload 包含 biduidnameavatarprofile_mode;其中 profile_mode 使用 overwritefill
  3. 对 Header 和 Payload 分别进行 Base64URL 编码(去掉 =),再使用 API 密钥对 headerBase64 + "." + payloadBase64 计算 HMAC-SHA256。
  4. 将签名进行 Base64URL 编码,按顺序拼接三部分,得到最终令牌。

系统收到令牌后会用同一业务的 API 密钥重新计算签名;签名一致后,才接受令牌中的用户 ID、昵称和头像。

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));

    $input = $header . '.' . $payload;
    $signature = base64url(hash_hmac('sha256', $input, $apiKey, true));
    return $input . '.' . $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 input = header64 + "." + payload64;

    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
    String signature64 = encoder.encodeToString(
            mac.doFinal(input.getBytes(StandardCharsets.UTF_8)));
    return input + "." + 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)
    input := header64 + "." + payload64
    mac := hmac.New(sha256.New, []byte(apiKey))
    mac.Write([]byte(input))
    return input + "." + encoder.EncodeToString(mac.Sum(nil)), nil
}

基础信息字段

普通传递和安全传递都可以传递以下基础信息:

信息普通传递配置安全传递凭证字段
用户 IDvisitor.uiduid
用户昵称visitor.nicknamename
用户头像visitor.avataravatar

最终效果

接入基础信息后,客服端会在会话标题和访客资料区域显示访客昵称、头像及用户 ID,便于客服识别当前访客。

用户基础信息在客服端的展示效果