Token
提示
社区版不支持,请升级到企业版或平台版。请替换licenseKey
功能简介
AccessToken 是微语系统提供的第三方登录凭证,主要用于第三方系统集成时避免用户二次登录。通过 AccessToken,第三方系统可以直接让用户登录到微语系统,无需用户重新输入用户名和密码。
使用场景
- 第三方系统集成: 当您的系统需要集成微语客服功能时
- 单点登录 (SSO): 实现用户一次登录即可访问多个系统
- 嵌入式应用: 在您的应用中嵌入微语客服界面
- API 调用: 通过 AccessToken 进行 API 认证,实现程序化访问微语系统的各项功能
AccessToken 登录方式
1. 获取 AccessToken
方式一:通过管理后台生成
在微语管理后台的 Token 管理页面,可以生成和管理 AccessToken。
方式二:通过登录接口获取
您也可以通过调用登录接口来获取 AccessToken,这种方式适用于程序化获取 Token 的场景。
接口说明
- 接口地址:
/auth/v1/login
,注意添加服务器域名或ip,如:http://127.0.0.1:9003/auth/v1/login
- 请求方法:
POST
- 内容类型:
application/json
TypeScript 类型定义
// 登录参数类型
interface LoginParams {
username?: string;
password?: string;
channel?: string;
platform: string;
}
// 登录返回结果类型
interface LoginResult {
message: string;
code: number;
data: {
accessToken?: string;
user?: any;
};
}
使用示例
import axios from 'axios';
// 配置登录参数
const loginInfo: LoginParams = {
username: 'your_username',
password: 'your_password',
channel: 'FLUTTER', // 渠道,固定值
platform: 'BYTEDESK' // 平台,固定值
};
// 登录接口封装
export async function login(params: LoginParams): Promise<LoginResult> {
try {
const response = await axios.post<LoginResult>('/auth/v1/login', {
...params,
});
return response.data;
} catch (error) {
console.error('登录失败:', error);
throw error;
}
}
// 获取 AccessToken
async function getAccessToken() {
try {
const result = await login(loginInfo);
if (result.code === 200 && result.data.accessToken) {
const accessToken = result.data.accessToken;
console.log('获取到 AccessToken:', accessToken);
// 使用 AccessToken 进行后续操作
return accessToken;
} else {
console.error('登录失败:', result.message);
return null;
}
} catch (error) {
console.error('获取 AccessToken 失败:', error);
return null;
}
}
// 完整示例:获取 Token 并跳转到系统
async function loginAndRedirect() {
const accessToken = await getAccessToken();
if (accessToken) {
// 使用获取到的 AccessToken 跳转到系统
const chatUrl = `http://127.0.0.1:9005/agent/chat?accessToken=${accessToken}`;
window.open(chatUrl, '_blank');
}
}
获取Token注意事项
- 安全性: 请勿在前端代码中硬编码用户名和密码
- 错误处理: 需要正确处理登录失败的情况
- Token 缓存: 可以将获取到的 AccessToken 缓存起来重复使用
- 有效期: 注意 AccessToken 的有效期,过期后需要重新获取
2. 撤销 AccessToken
当 AccessToken 不再需要使用时,为了安全起见,建议及时撤销使其失效。系统提供了两种撤销方式:
方式一:通过管理后台撤销 或 直接退出登录撤销
在微语管理后台的 Token 管理页面,可以手动撤销指定的 AccessToken,使其立即失效。
方式二:通过登出接口撤销
您也可以通过调用登出接口来撤销当前的 AccessToken,这种方式适用于程序化撤销 Token 的场景。
撤销接口说明
- 接口地址:
/api/v1/user/logout
,注意添加服务器域名或ip,如:http://127.0.0.1:9003/api/v1/user/logout
- 请求方法:
POST
- 认证方式: Bearer Token(在请求头中设置 AccessToken)
撤销返回类型定义
// 登出返回结果类型
interface LogoutResult {
message: string;
code: number;
data: boolean;
}
撤销使用示例
import axios from 'axios';
// 配置 axios 实例
const request = axios.create({
baseURL: 'http://your-domain.com', // 替换为实际的服务器地址
timeout: 10000,
});
// 登出接口封装
export async function logout(accessToken: string): Promise<LogoutResult> {
try {
const response = await request.post<LogoutResult>('/api/v1/user/logout', {}, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
console.error('登出失败:', error);
throw error;
}
}
// 撤销 AccessToken 示例
async function revokeAccessToken(accessToken: string) {
try {
const result = await logout(accessToken);
if (result.code === 200 && result.data === true) {
console.log('AccessToken 撤销成功:', result.message);
// 清除本地存储的 Token
localStorage.removeItem('accessToken');
sessionStorage.removeItem('accessToken');
return true;
} else {
console.error('AccessToken 撤销失败:', result.message);
return false;
}
} catch (error) {
console.error('撤销 AccessToken 失败:', error);
return false;
}
}
// 完整示例:撤销 Token 并重定向
async function logoutAndRedirect(accessToken: string) {
const success = await revokeAccessToken(accessToken);
if (success) {
// 撤销成功后重定向到登录页面
window.location.href = '/login';
}
}
请求拦截器配置示例
如果您使用 axios 拦截器统一处理认证头,可以参考以下配置:
import axios from 'axios';
// 创建 axios 实例
const request = axios.create({
baseURL: 'http://your-domain.com',
timeout: 10000,
});
// 请求拦截器:自动添加 Authorization 头
request.interceptors.request.use(
(config) => {
// 从存储中获取 AccessToken
const accessToken = localStorage.getItem('accessToken') ||
sessionStorage.getItem('accessToken');
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器:处理 Token 失效
request.interceptors.response.use(
(response) => {
return response;
},
(error) => {
// 如果返回 401,说明 Token 已失效
if (error.response?.status === 401) {
// 清除失效的 Token
localStorage.removeItem('accessToken');
sessionStorage.removeItem('accessToken');
// 重定向到登录页面
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// 使用拦截器配置后的登出接口
export async function logout(): Promise<LogoutResult> {
try {
const response = await request.post<LogoutResult>('/api/v1/user/logout');
return response.data;
} catch (error) {
console.error('登出失败:', error);
throw error;
}
}
撤销注意事项
- 认证头格式: 必须使用
Bearer ${accessToken}
格式设置 Authorization 头 - Token 清理: 撤销成功后应及时清除本地存储的 Token
- 错误处理: 需要正确处理网络错误和服务器返回的错误
- 安全性: 撤销 Token 后应引导用户重新登录
- 状态管理: 在单页应用中注意更新应用的登录状态
3. 任意路径登录优势
AccessToken 支持任意路径登录,这意味着您可以直接跳转到系统的任何页面,而不仅限于登录页面。这种方式的优势包括:
- 提升用户体验: 用户可以直接跳转到需要的功能页面,无需额外的导航步骤
- 简化集成流程: 第三方系统可以直接链接到具体功能模块
- 减少页面跳转: 避免先跳转到登录页面再跳转到目标页面的多余步骤
- 精确定位: 可以将用户直接带到相关的工作页面,提高工作效率
4. 登录 URL 格式
AccessToken 支持两种登录方式,并且支持任意路径登录:
客服工作台登录
# 标准登录页面
http://服务器ip/agent/auth/login?accessToken=YOUR_ACCESS_TOKEN
# 任意路径登录(推荐)
http://服务器ip/agent/chat?accessToken=YOUR_ACCESS_TOKEN
http://服务器ip/agent/ticket?accessToken=YOUR_ACCESS_TOKEN
http://服务器ip/agent/任意路径?accessToken=YOUR_ACCESS_TOKEN
管理后台登录
# 标准登录页面
http://服务器ip/admin/auth/login?accessToken=YOUR_ACCESS_TOKEN
# 任意路径登录(推荐)
http://服务器ip/admin/welcome?accessToken=YOUR_ACCESS_TOKEN
http://服务器ip/admin/dashboard?accessToken=YOUR_ACCESS_TOKEN
http://服务器ip/admin/任意路径?accessToken=YOUR_ACCESS_TOKEN
5. 完整示例
客服工作台示例
# 标准登录页面
http://your-domain.com/agent/auth/login?accessToken=eyJhbGciOiJIUzM4NCJ9...
# 聊天页面直接登录
http://127.0.0.1:9005/agent/chat?accessToken=eyJhbGciOiJIUzM4NCJ9...
# 工单页面直接登录
http://127.0.0.1:9005/agent/ticket?accessToken=eyJhbGciOiJIUzM4NCJ9...
管理后台示例
# 标准登录页面
http://your-domain.com/admin/auth/login?accessToken=eyJhbGciOiJIUzM4NCJ9...
# 欢迎页面直接登录
http://127.0.0.1:9004/admin/welcome?accessToken=eyJhbGciOiJIUzM4NCJ9...
# 仪表盘页面直接登录
http://127.0.0.1:9004/admin/dashboard?accessToken=eyJhbGciOiJIUzM4NCJ9...
集成示例
1. 简单链接跳转
客服工作台
<!-- 标准登录页面 -->
<a href="http://your-domain.com/agent/auth/login?accessToken=YOUR_ACCESS_TOKEN" target="_blank">
打开客服工作台
</a>
<!-- 直接跳转到聊天页面 -->
<a href="http://127.0.0.1:9005/agent/chat?accessToken=YOUR_ACCESS_TOKEN" target="_blank">
打开客服聊天页面
</a>
<!-- 直接跳转到工单页面 -->
<a href="http://127.0.0.1:9005/agent/ticket?accessToken=YOUR_ACCESS_TOKEN" target="_blank">
打开客服工单页面
</a>
管理后台
<!-- 标准登录页面 -->
<a href="http://your-domain.com/admin/auth/login?accessToken=YOUR_ACCESS_TOKEN" target="_blank">
打开管理后台
</a>
<!-- 直接跳转到欢迎页面 -->
<a href="http://127.0.0.1:9004/admin/welcome?accessToken=YOUR_ACCESS_TOKEN" target="_blank">
打开管理后台欢迎页
</a>
<!-- 直接跳转到仪表盘 -->
<a href="http://127.0.0.1:9004/admin/dashboard?accessToken=YOUR_ACCESS_TOKEN" target="_blank">
打开管理后台仪表盘
</a>
2. JavaScript 跳转
// 打开客服工作台(标准登录)
function openAgentWorkspace(accessToken) {
const url = `http://your-domain.com/agent/auth/login?accessToken=${accessToken}`;
window.open(url, '_blank');
}
// 打开客服聊天页面(任意路径登录)
function openAgentChat(accessToken) {
const url = `http://127.0.0.1:9005/agent/chat?accessToken=${accessToken}`;
window.open(url, '_blank');
}
// 打开客服工单页面(任意路径登录)
function openAgentTicket(accessToken) {
const url = `http://127.0.0.1:9005/agent/ticket?accessToken=${accessToken}`;
window.open(url, '_blank');
}
// 打开管理后台(标准登录)
function openAdminPanel(accessToken) {
const url = `http://your-domain.com/admin/auth/login?accessToken=${accessToken}`;
window.open(url, '_blank');
}
// 打开管理后台欢迎页(任意路径登录)
function openAdminWelcome(accessToken) {
const url = `http://127.0.0.1:9004/admin/welcome?accessToken=${accessToken}`;
window.open(url, '_blank');
}
// 打开管理后台仪表盘(任意路径登录)
function openAdminDashboard(accessToken) {
const url = `http://127.0.0.1:9004/admin/dashboard?accessToken=${accessToken}`;
window.open(url, '_blank');
}
// 使用示例
const accessToken = 'YOUR_ACCESS_TOKEN';
openAgentChat(accessToken); // 直接打开聊天页面
openAgentTicket(accessToken); // 直接打开工单页面
openAdminWelcome(accessToken); // 直接打开管理后台欢迎页
openAdminDashboard(accessToken); // 直接打开管理后台仪表盘
3. iframe 嵌入
客服工作台iframe示例
<!-- 标准登录页面 -->
<iframe
src="http://your-domain.com/agent/auth/login?accessToken=YOUR_ACCESS_TOKEN"
width="100%"
height="600px"
frameborder="0">
</iframe>
<!-- 直接嵌入聊天页面 -->
<iframe
src="http://127.0.0.1:9005/agent/chat?accessToken=YOUR_ACCESS_TOKEN"
width="100%"
height="600px"
frameborder="0">
</iframe>
<!-- 直接嵌入工单页面 -->
<iframe
src="http://127.0.0.1:9005/agent/ticket?accessToken=YOUR_ACCESS_TOKEN"
width="100%"
height="600px"
frameborder="0">
</iframe>
管理后台iframe示例
<!-- 标准登录页面 -->
<iframe
src="http://your-domain.com/admin/auth/login?accessToken=YOUR_ACCESS_TOKEN"
width="100%"
height="600px"
frameborder="0">
</iframe>
<!-- 直接嵌入欢迎页面 -->
<iframe
src="http://127.0.0.1:9004/admin/welcome?accessToken=YOUR_ACCESS_TOKEN"
width="100%"
height="600px"
frameborder="0">
</iframe>
<!-- 直接嵌入仪表盘 -->
<iframe
src="http://127.0.0.1:9004/admin/dashboard?accessToken=YOUR_ACCESS_TOKEN"
width="100%"
height="600px"
frameborder="0">
</iframe>
常用 API 端点示例
- 工单管理:
/api/tickets/*
- 工单的增删改查操作 - 用户管理:
/api/users/*
- 用户信息管理 - 消息管理:
/api/messages/*
- 消息发送和查询 - 统计报表:
/api/statistics/*
- 数据统计和报表 - 系统配置:
/api/settings/*
- 系统配置管理
创建用户接口演示
权限要求
创建用户接口仅限超级管理员账号调用。系统安装完成后 会自动创建超级管理员账号,需要使用超级管理员登录后获取 AccessToken 来调用此接口。
1. 接口说明
- 接口地址:
/api/v1/user/create
- 请求方法:
POST
- 内容类型:
application/json
- 认证方式: Bearer Token(超级管理员权限)
2. TypeScript 类型定义
// 性别枚举
enum Sex {
MALE = 'MALE', // 男性
FEMALE = 'FEMALE', // 女性
UNKNOWN = 'UNKNOWN' // 未知
}
// 创建用户请求参数类型
interface UserRequest {
username?: string; // 用户名(可选,默认使用邮箱或手机号)
nickname?: string; // 用户昵称(可选,系统会自动生成默认昵称)
password?: string; // 用户密码(可选,但建议设置)
email?: string; // 邮箱地址(邮箱或手机号至少提供一个)
mobile?: string; // 手机号码(邮箱或手机号至少提供一个)
avatar?: string; // 用户头像URL(可选,默认使用系统头像)
description?: string; // 用户描述/个人简介(可选)
emailVerified?: boolean; // 邮箱是否已验证(可选,默认为false)
mobileVerified?: boolean; // 手机号是否已验证(可选,默认为true)
platform: string; // 平台标识(必填,固定写死为BYTEDESK)
}
// 用户响应类型
interface UserResponse {
uid: string; // 用户唯一标识符
username: string; // 用户名
nickname: string; // 用户昵称
email?: string; // 邮箱地址
mobile?: string; // 手机号码
country?: string; // 国家代码
avatar: string; // 用户头像URL
description: string; // 用户描述/个人简介
platform: string; // 平台标识
sex: Sex; // 用户性别
enabled: boolean; // 账户是否启用
superUser: boolean; // 是否为超级用户
emailVerified: boolean; // 邮箱是否已验证
mobileVerified: boolean; // 手机号是否已验证
passwordModifiedAt: string; // 密码最后修改时间(ISO格式)
currentOrganization?: any; // 当前所属组织信息
currentRoles?: any[]; // 当前用户角色列表
userOrganizationRoles?: any[]; // 用户组织角色关系列表
authorities?: any[]; // 用户权限列表
}
// 创建用户返回结果类型
interface CreateUserResult {
message: string; // 响应消息
code: number; // 响应状态码(200表示成功)
data: UserResponse; // 创建成功的用户信息
}
3. 接口使用示例
基础使用示例
import axios from 'axios';
// 配置 axios 实例
const apiClient = axios.create({
baseURL: 'http://127.0.0.1:9003', // 替换为实际的服务器地址
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// 创建用户接口封装
export async function createUser(
userRequest: UserRequest,
accessToken: string
): Promise<CreateUserResult> {
try {
const response = await apiClient.post<CreateUserResult>('/api/v1/user/create', userRequest, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
return response.data;
} catch (error) {
console.error('创建用户失败:', error);
throw error;
}
}
// 创建用户示例函数
async function createNewUser(superAdminToken: string) {
// 创建用户参数
const userRequest: UserRequest = {
username: 'newuser@example.com',
email: 'newuser@example.com',
password: 'SecurePassword123!',
nickname: '新用户',
mobile: '13812345678',
emailVerified: true,
mobileVerified: true,
platform: 'BYTEDESK', // 平台标识,必填,固定写死为BYTEDESK
description: '通过API创建的用户'
};
try {
const result = await createUser(userRequest, superAdminToken);
if (result.code === 200) {
console.log('用户创建成功:', result.data);
return result.data;
} else {
console.error('用户创建失败:', result.message);
return null;
}
} catch (error) {
console.error('创建用户时发生错误:', error);
return null;
}
}
完整流程示例
// 完整的创建用户流程:登录获取超级管理员Token -> 创建用户
async function completeUserCreationFlow() {
try {
// 步骤1: 使用超级管理员账号登录
const loginParams = {
username: 'admin@example.com', // 超级管理员邮箱
password: 'AdminPassword123!', // 超级管理员密码
channel: 'FLUTTER',
platform: 'BYTEDESK'
};
const loginResult = await login(loginParams);
if (loginResult.code !== 200 || !loginResult.data.accessToken) {
throw new Error(`登录失败: ${loginResult.message}`);
}
const superAdminToken = loginResult.data.accessToken;
console.log('超级管理员登录成功');
// 步骤2: 使用超级管理员Token创建新用户
const newUserData = await createNewUser(superAdminToken);
if (newUserData) {
console.log('用户创建流程完成:', {
username: newUserData.username,
nickname: newUserData.nickname,
email: newUserData.email,
mobile: newUserData.mobile,
enabled: newUserData.enabled
});
}
return newUserData;
} catch (error) {
console.error('完整用户创建流程失败:', error);
throw error;
}
}
4. 不同创建场景示例
仅邮箱注册
const emailOnlyUser: UserRequest = {
email: 'user1@example.com',
password: 'Password123!',
nickname: '邮箱用户',
emailVerified: true,
platform: 'BYTEDESK'
};
仅手机号注册
const mobileOnlyUser: UserRequest = {
mobile: '13987654321',
password: 'Password123!',
nickname: '手机用户',
mobileVerified: true,
platform: 'BYTEDESK'
};
邮箱+手机号注册
const fullUser: UserRequest = {
username: 'fulluser',
email: 'fulluser@example.com',
mobile: '13612345678',
password: 'Password123!',
nickname: '完整用户',
avatar: 'https://example.com/avatar.jpg',
description: '拥有完整信息的用户',
emailVerified: true,
mobileVerified: true,
platform: 'BYTEDESK'
};
5. 错误处理示例
async function createUserWithErrorHandling(
userRequest: UserRequest,
accessToken: string
) {
try {
const result = await createUser(userRequest, accessToken);
return result;
} catch (error: any) {
// 处理不同类型的错误
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 400:
if (data.message?.includes('Email') && data.message?.includes('already exists')) {
console.error('邮箱已存在:', data.message);
} else if (data.message?.includes('Mobile') && data.message?.includes('already exists')) {
console.error('手机号已存在:', data.message);
} else if (data.message?.includes('email or mobile is required')) {
console.error('邮箱或手机号必填:', data.message);
} else {
console.error('请求参数错误:', data.message);
}
break;
case 401:
console.error('认证失败,请检查AccessToken是否有效');
break;
case 403:
console.error('权限不足,只有超级管理员才能创建用户');
break;
case 500:
console.error('服务器内部错误:', data.message);
break;
default:
console.error('未知错误:', data.message || error.message);
}
} else if (error.request) {
console.error('网络请求失败,请检查网络连接');
} else {
console.error('请求配置错误:', error.message);
}
throw error;
}
}
6. 批量创建用户示例
async function batchCreateUsers(
userRequests: UserRequest[],
superAdminToken: string
) {
const results = [];
const errors = [];
for (let i = 0; i < userRequests.length; i++) {
const userRequest = userRequests[i];
try {
console.log(`正在创建第 ${i + 1} 个用户...`);
const result = await createUser(userRequest, superAdminToken);
if (result.code === 200) {
results.push(result.data);
console.log(`✅ 用户 ${userRequest.username || userRequest.email || userRequest.mobile} 创建成功`);
} else {
errors.push({ user: userRequest, error: result.message });
console.log(`❌ 用户 ${userRequest.username || userRequest.email || userRequest.mobile} 创建失败: ${result.message}`);
}
// 避免请求过于频繁,添加延迟
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error: any) {
errors.push({ user: userRequest, error: error.message });
console.log(`❌ 用户 ${userRequest.username || userRequest.email || userRequest.mobile} 创建异常: ${error.message}`);
}
}
return {
successCount: results.length,
errorCount: errors.length,
results,
errors
};
}
// 批量创建用户使用示例
async function batchCreateExample(superAdminToken: string) {
const usersToCreate: UserRequest[] = [
{
email: 'user1@example.com',
password: 'Password123!',
nickname: '用户1',
platform: 'BYTEDESK'
},
{
email: 'user2@example.com',
password: 'Password123!',
nickname: '用户2',
platform: 'BYTEDESK'
},
{
mobile: '13812345678',
password: 'Password123!',
nickname: '用户3',
platform: 'BYTEDESK'
}
];
const batchResult = await batchCreateUsers(usersToCreate, superAdminToken);
console.log('批量创建结果:', {
成功: batchResult.successCount,
失败: batchResult.errorCount,
成功用户: batchResult.results.map(u => u.username),
失败详情: batchResult.errors
});
}