验证 Login Ticket
验证 Login Ticket 并获取用户信息。
接口说明
| 属性 | 值 |
|---|---|
| 请求方式 | POST |
| 请求路径 | /im-user/api/v1/oauth/ticket/verify |
| 鉴权方式 | Bot Token |
重要
此接口由第三方应用后端调用,需要使用 Bot Token 进行鉴权。请确保在服务端调用,不要在前端暴露 Bot Token。
请求头
Authorization: Bearer {botToken}
Content-Type: application/json
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
ticket | string | 是 | 登录票据(从 URL 参数中获取) |
请求示例
curl -X POST 'https://api.im.example.com/im-user/api/v1/oauth/ticket/verify' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
-H 'Content-Type: application/json' \
-d '{
"ticket": "lt_550e8400e29b41d4a716446655440000"
}'
响应参数
| 参数 | 类型 | 说明 |
|---|---|---|
imUserId | number | IM 用户 ID |
imUserName | string | IM 用户名称 |
imUserPhone | string | IM 用户手机号(脱敏,如 138****1234) |
imUserPhoneFull | string | IM 用户手机号(完整,仅在应用有相应权限时返回) |
imUserAvatar | string | IM 用户头像 URL |
imWorkspaceId | number | IM 工作空间 ID |
imWorkspaceName | string | IM 工作空间名称 |
响应示例
成功响应
{
"code": 200,
"data": {
"imUserId": 123,
"imUserName": "张三",
"imUserPhone": "138****1234",
"imUserPhoneFull": "13812341234",
"imUserAvatar": "https://cdn.example.com/avatar/123.jpg",
"imWorkspaceId": 1001,
"imWorkspaceName": "测试工作空间"
}
}
失败响应
{
"code": 400,
"msg": "Login Ticket 无效或已过期"
}
错误码
| 错误码 | 说明 | 解决方案 |
|---|---|---|
| 400 | ticket不能为空 | 确保传递了 ticket 参数 |
| 400 | Login Ticket 无效或已过期 | Ticket 已使用或超过 5 分钟,请重新从 IM 打开应用 |
| 401 | 缺少 Bot Token | 确保在请求头中传递了 Authorization |
| 401 | Bot Token 无效或已过期 | 检查 Bot Token 是否正确,或重新安装应用 |
| 401 | 需要使用 Bot Token 调用此接口 | 确保使用 Bot Token 而非 User Token |
| 401 | Bot Token 信息不完整 | Bot Token 格式错误,请重新安装应用 |
Java 调用示例
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import java.util.HashMap;
import java.util.Map;
public class ImApiClient {
private final String apiBaseUrl;
public ImApiClient(String apiBaseUrl) {
this.apiBaseUrl = apiBaseUrl;
}
/**
* 验证 Login Ticket
*
* @param botToken Bot Token
* @param ticket 登录票据
* @return 用户信息
*/
public TicketVerifyResponse verifyTicket(String botToken, String ticket) {
Map<String, Object> body = new HashMap<>();
body.put("ticket", ticket);
String response = HttpRequest.post(apiBaseUrl + "/im-user/api/v1/oauth/ticket/verify")
.header("Authorization", "Bearer " + botToken)
.header("Content-Type", "application/json")
.body(JSONUtil.toJsonStr(body))
.execute()
.body();
JSONObject json = JSONUtil.parseObj(response);
if (json.getInt("code") != 200) {
throw new RuntimeException("验证 Ticket 失败: " + json.getStr("msg"));
}
return json.getBean("data", TicketVerifyResponse.class);
}
}
Python 调用示例
import requests
class ImApiClient:
def __init__(self, api_base_url: str):
self.api_base_url = api_base_url
def verify_ticket(self, bot_token: str, ticket: str) -> dict:
"""验证 Login Ticket"""
url = f"{self.api_base_url}/im-user/api/v1/oauth/ticket/verify"
headers = {
"Authorization": f"Bearer {bot_token}",
"Content-Type": "application/json"
}
payload = {"ticket": ticket}
response = requests.post(url, json=payload, headers=headers)
result = response.json()
if result.get("code") != 200:
raise Exception(f"验证 Ticket 失败: {result.get('msg')}")
return result.get("data")
使用场景
验证 Ticket 后,根据返回的用户信息查询绑定关系:
@GetMapping("/api/im/sso/entry")
public String handleSsoEntry(
@RequestParam("ticket") String ticket,
@RequestParam("workspace_id") Long workspaceId
) {
// 1. 获取 Bot Token
WorkspaceBinding binding = workspaceBindingService.getByWorkspaceId(workspaceId);
if (binding == null) {
return "redirect:/error?msg=应用未安装";
}
String botToken = binding.getBotToken();
// 2. 验证 Ticket
TicketVerifyResponse ticketInfo = imApiClient.verifyTicket(botToken, ticket);
// 3. 查询用户绑定关系
ImBinding userBinding = imBindingService.getByImUser(
ticketInfo.getImUserId().toString(),
workspaceId
);
if (userBinding != null) {
// 已绑定,生成登录态
User user = userService.getById(userBinding.getUserId());
String token = jwtService.generateToken(user);
return "redirect:/home?token=" + token;
} else {
// 未绑定,跳转到绑定页面
return "redirect:/im/bind?im_user_id=" + ticketInfo.getImUserId()
+ "&im_user_name=" + URLEncoder.encode(ticketInfo.getImUserName(), "UTF-8");
}
}