Appearance
用户基础信息对接
用户基础信息对接用于把业务系统中的用户身份传递给客服系统。支持传递用户 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 三部分组成:
- Header 固定为
{"alg":"HS256","typ":"JWT"}。 - Payload 包含
bid、uid、name、avatar、profile_mode;其中profile_mode使用overwrite或fill。 - 对 Header 和 Payload 分别进行 Base64URL 编码(去掉
=),再使用 API 密钥对headerBase64 + "." + payloadBase64计算 HMAC-SHA256。 - 将签名进行 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
}基础信息字段
普通传递和安全传递都可以传递以下基础信息:
| 信息 | 普通传递配置 | 安全传递凭证字段 |
|---|---|---|
| 用户 ID | visitor.uid | uid |
| 用户昵称 | visitor.nickname | name |
| 用户头像 | visitor.avatar | avatar |
最终效果
接入基础信息后,客服端会在会话标题和访客资料区域显示访客昵称、头像及用户 ID,便于客服识别当前访客。

