chore(git): 更新.gitignore以忽略新的本地文件
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
# Yggdrasil协议API
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [routes.go](file://internal/handler/routes.go)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go)
|
||||
- [yggdrasil.go](file://internal/model/yggdrasil.go)
|
||||
- [token.go](file://internal/model/token.go)
|
||||
- [user_service.go](file://internal/service/user_service.go)
|
||||
- [profile_service.go](file://internal/service/profile_service.go)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go)
|
||||
- [jwt.go](file://pkg/auth/jwt.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [Yggdrasil路由结构](#yggdrasil路由结构)
|
||||
3. [认证服务API](#认证服务api)
|
||||
4. [会话服务API](#会话服务api)
|
||||
5. [档案服务API](#档案服务api)
|
||||
6. [与Minecraft客户端交互流程](#与minecraft客户端交互流程)
|
||||
7. [内部用户系统集成](#内部用户系统集成)
|
||||
8. [错误处理](#错误处理)
|
||||
9. [元数据与证书](#元数据与证书)
|
||||
|
||||
## 简介
|
||||
Yggdrasil协议是Minecraft客户端认证的核心接口,本系统实现了完整的Yggdrasil协议集成,为Minecraft玩家提供认证、会话管理和档案查询服务。该实现通过`/yggdrasil`路由组暴露标准API端点,与Minecraft客户端无缝对接,同时将Yggdrasil请求映射到系统的内部用户体系。
|
||||
|
||||
**Section sources**
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
|
||||
## Yggdrasil路由结构
|
||||
系统在`/api/v1/yggdrasil`路径下提供了完整的Yggdrasil协议支持,包含三个主要子路由组:`authserver`、`sessionserver`和`profiles`,分别处理认证、会话和档案相关请求。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Yggdrasil[Yggdrasil根路由 /yggdrasil]
|
||||
subgraph AuthServer
|
||||
Authenticate[POST /authserver/authenticate]
|
||||
Validate[POST /authserver/validate]
|
||||
Refresh[POST /authserver/refresh]
|
||||
Invalidate[POST /authserver/invalidate]
|
||||
SignOut[POST /authserver/signout]
|
||||
end
|
||||
subgraph SessionServer
|
||||
GetProfile[GET /sessionserver/session/minecraft/profile/:uuid]
|
||||
JoinServer[POST /sessionserver/session/minecraft/join]
|
||||
HasJoined[GET /sessionserver/session/minecraft/hasJoined]
|
||||
end
|
||||
subgraph Profiles
|
||||
GetProfilesByName[POST /api/profiles/minecraft]
|
||||
end
|
||||
Yggdrasil --> Authenticate
|
||||
Yggdrasil --> Validate
|
||||
Yggdrasil --> Refresh
|
||||
Yggdrasil --> Invalidate
|
||||
Yggdrasil --> SignOut
|
||||
Yggdrasil --> GetProfile
|
||||
Yggdrasil --> JoinServer
|
||||
Yggdrasil --> HasJoined
|
||||
Yggdrasil --> GetProfilesByName
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
|
||||
**Section sources**
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
|
||||
## 认证服务API
|
||||
认证服务API位于`/yggdrasil/authserver`路径下,提供用户认证和令牌管理功能。
|
||||
|
||||
### authenticate
|
||||
用户认证端点,用于验证用户凭据并获取访问令牌。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/authserver/authenticate`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"username": "用户邮箱或用户名",
|
||||
"password": "密码",
|
||||
"clientToken": "客户端令牌"
|
||||
}
|
||||
```
|
||||
- **响应**:
|
||||
- 200: 返回包含`accessToken`、`availableProfiles`和`selectedProfile`的认证响应
|
||||
- 403: 认证失败
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L246)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L204-L209)
|
||||
|
||||
### validate
|
||||
验证访问令牌的有效性。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/authserver/validate`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"accessToken": "访问令牌"
|
||||
}
|
||||
```
|
||||
- **响应**:
|
||||
- 204: 令牌有效
|
||||
- 403: 令牌无效
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L248-L267)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L260-L261)
|
||||
|
||||
### refresh
|
||||
刷新访问令牌,延长会话有效期。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/authserver/refresh`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"accessToken": "当前访问令牌",
|
||||
"clientToken": "客户端令牌",
|
||||
"selectedProfile": "选定的档案"
|
||||
}
|
||||
```
|
||||
- **响应**:
|
||||
- 200: 返回新的`accessToken`和`selectedProfile`
|
||||
- 400: 刷新失败
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L269-L361)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L341-L351)
|
||||
|
||||
### invalidate
|
||||
使访问令牌失效。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/authserver/invalidate`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"accessToken": "要使失效的令牌"
|
||||
}
|
||||
```
|
||||
- **响应**:
|
||||
- 204: 成功使令牌失效
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L363-L378)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L375)
|
||||
|
||||
### signout
|
||||
用户登出,使该用户的所有令牌失效。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/authserver/signout`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"username": "用户邮箱",
|
||||
"password": "密码"
|
||||
}
|
||||
```
|
||||
- **响应**:
|
||||
- 204: 登出成功
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L380-L425)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L422)
|
||||
|
||||
## 会话服务API
|
||||
会话服务API位于`/yggdrasil/sessionserver`路径下,管理玩家与服务器的会话。
|
||||
|
||||
### JoinServer
|
||||
记录玩家加入服务器的会话信息。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/sessionserver/session/minecraft/join`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"accessToken": "访问令牌",
|
||||
"selectedProfile": "选定的档案UUID",
|
||||
"serverId": "服务器ID"
|
||||
}
|
||||
```
|
||||
- **响应**:
|
||||
- 204: 加入成功
|
||||
- 500: 加入失败
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "Minecraft客户端"
|
||||
participant SessionServer as "会话服务"
|
||||
participant Redis as "Redis存储"
|
||||
Client->>SessionServer : POST /join
|
||||
SessionServer->>SessionServer : 验证accessToken
|
||||
SessionServer->>SessionServer : 验证selectedProfile
|
||||
SessionServer->>Redis : 存储会话数据
|
||||
Redis-->>SessionServer : 存储成功
|
||||
SessionServer-->>Client : 204 No Content
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L449-L496)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L163)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L449-L496)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L163)
|
||||
|
||||
### HasJoinedServer
|
||||
验证玩家是否已加入指定服务器。
|
||||
|
||||
- **方法**: GET
|
||||
- **路径**: `/yggdrasil/sessionserver/session/minecraft/hasJoined`
|
||||
- **查询参数**:
|
||||
- `serverId`: 服务器ID
|
||||
- `username`: 用户名
|
||||
- `ip`: IP地址(可选)
|
||||
- **响应**:
|
||||
- 200: 返回玩家档案信息
|
||||
- 204: 未加入或验证失败
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L498-L552)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L165-L201)
|
||||
|
||||
## 档案服务API
|
||||
档案服务API提供玩家档案查询功能。
|
||||
|
||||
### GetProfileByUUID
|
||||
根据UUID获取玩家档案。
|
||||
|
||||
- **方法**: GET
|
||||
- **路径**: `/yggdrasil/sessionserver/session/minecraft/profile/:uuid`
|
||||
- **响应**:
|
||||
- 200: 返回玩家档案信息
|
||||
- 500: 获取失败
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L427-L447)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L80)
|
||||
|
||||
### GetProfilesByName
|
||||
根据用户名批量获取玩家档案。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/api/profiles/minecraft`
|
||||
- **请求体**: 用户名数组
|
||||
```json
|
||||
["player1", "player2"]
|
||||
```
|
||||
- **响应**: 返回匹配的档案列表
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L554-L587)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L129-L137)
|
||||
|
||||
## 与Minecraft客户端交互流程
|
||||
Minecraft客户端与Yggdrasil服务的完整交互流程如下:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "Minecraft客户端"
|
||||
participant AuthServer as "认证服务"
|
||||
participant SessionServer as "会话服务"
|
||||
participant ProfileServer as "档案服务"
|
||||
Client->>AuthServer : authenticate(邮箱/用户名, 密码)
|
||||
AuthServer-->>Client : accessToken, availableProfiles
|
||||
Client->>SessionServer : join(serverId, accessToken, selectedProfile)
|
||||
SessionServer-->>Client : 204
|
||||
Client->>ProfileServer : hasJoined(serverId, username)
|
||||
ProfileServer-->>Client : 玩家档案
|
||||
Client->>ProfileServer : getProfile(uuid)
|
||||
ProfileServer-->>Client : 档案详情
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go)
|
||||
|
||||
## 内部用户系统集成
|
||||
Yggdrasil实现与系统内部用户体系的集成通过以下方式完成:
|
||||
|
||||
### 用户映射
|
||||
系统通过`Yggdrasil`模型将Yggdrasil协议的密码认证与内部用户ID关联。当用户注册时,系统自动生成一个16位随机密码与用户ID绑定。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class User {
|
||||
+int64 ID
|
||||
+string Username
|
||||
+string Email
|
||||
+string Password
|
||||
}
|
||||
class Yggdrasil {
|
||||
+int64 ID
|
||||
+string Password
|
||||
}
|
||||
User --> Yggdrasil : "1对1关联"
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil.go](file://internal/model/yggdrasil.go)
|
||||
- [user_service.go](file://internal/service/user_service.go#L13-L67)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil.go](file://internal/model/yggdrasil.go#L13-L38)
|
||||
- [user_service.go](file://internal/service/user_service.go#L13-L67)
|
||||
|
||||
### 令牌管理
|
||||
系统使用`Token`模型管理访问令牌,将`accessToken`与`userID`、`profileId`关联,实现会话状态管理。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Token {
|
||||
+string AccessToken
|
||||
+int64 UserID
|
||||
+string ProfileId
|
||||
+bool Usable
|
||||
}
|
||||
class Profile {
|
||||
+string UUID
|
||||
+int64 UserID
|
||||
+string Name
|
||||
}
|
||||
Token --> Profile : "关联档案"
|
||||
Token --> User : "关联用户"
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [token.go](file://internal/model/token.go)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L211-L216)
|
||||
|
||||
**Section sources**
|
||||
- [token.go](file://internal/model/token.go)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L211-L216)
|
||||
|
||||
## 错误处理
|
||||
系统实现了标准化的错误处理机制,所有Yggdrasil端点返回一致的错误响应格式。
|
||||
|
||||
### 常见错误代码
|
||||
| HTTP状态码 | 错误类型 | 描述 |
|
||||
|-----------|---------|------|
|
||||
| 400 | Bad Request | 请求格式无效 |
|
||||
| 403 | Forbidden | 认证失败或权限不足 |
|
||||
| 404 | Not Found | 资源未找到 |
|
||||
| 500 | Internal Server Error | 服务器内部错误 |
|
||||
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"error": "密码错误"
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L19-L57)
|
||||
|
||||
## 元数据与证书
|
||||
系统提供元数据和玩家证书服务,支持Minecraft客户端的高级功能。
|
||||
|
||||
### GetMetaData
|
||||
获取服务元数据。
|
||||
|
||||
- **方法**: GET
|
||||
- **路径**: `/yggdrasil`
|
||||
- **响应**: 包含实现信息、链接和功能标志
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L589-L615)
|
||||
|
||||
### GetPlayerCertificates
|
||||
获取玩家证书。
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/yggdrasil/minecraftservices/player/certificates`
|
||||
- **认证**: Bearer Token
|
||||
- **响应**: 包含玩家公钥证书
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L618-L667)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L44-L48)
|
||||
305
.qoder/repowiki/zh/content/API参考/Yggdrasil协议API/会话服务.md
Normal file
305
.qoder/repowiki/zh/content/API参考/Yggdrasil协议API/会话服务.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# 会话服务
|
||||
|
||||
<cite>
|
||||
**本文档引用文件**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go)
|
||||
- [yggdrasil_service_test.go](file://internal/service/yggdrasil_service_test.go)
|
||||
- [routes.go](file://internal/handler/routes.go)
|
||||
- [profile.go](file://internal/model/profile.go)
|
||||
- [profile_service.go](file://internal/service/profile_service.go)
|
||||
- [redis.go](file://pkg/redis/redis.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [API路由结构](#api路由结构)
|
||||
3. [核心API功能详解](#核心api功能详解)
|
||||
4. [会话数据结构](#会话数据结构)
|
||||
5. [反作弊机制](#反作弊机制)
|
||||
6. [与Minecraft客户端交互流程](#与minecraft客户端交互流程)
|
||||
7. [错误处理与日志](#错误处理与日志)
|
||||
8. [测试验证](#测试验证)
|
||||
|
||||
## 简介
|
||||
本文档详细描述了Yggdrasil会话服务的核心功能,重点聚焦于`/sessionserver`路由组下的三个关键API:`GetProfileByUUID`、`JoinServer`和`HasJoinedServer`。这些API构成了Minecraft服务器会话验证系统的核心,负责处理玩家加入服务器的会话建立、验证和玩家档案查询。
|
||||
|
||||
系统通过Redis存储会话数据,利用`Join_`为前缀的键名和15分钟的TTL(生存时间)来管理会话生命周期。整个流程确保了只有经过身份验证的玩家才能加入服务器,同时通过IP地址和用户名的双重验证机制防止作弊行为。
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L1-L100)
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
|
||||
## API路由结构
|
||||
会话服务的API路由定义在`routes.go`文件中,位于`/yggdrasil/sessionserver`路径下。该路由组提供了三个核心端点:
|
||||
|
||||
- `GET /session/minecraft/profile/:uuid`:根据玩家UUID获取其公开档案信息。
|
||||
- `POST /session/minecraft/join`:接收客户端的会话信息,建立服务器会话。
|
||||
- `GET /session/minecraft/hasJoined`:验证某玩家是否已成功加入指定服务器。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Yggdrasil API"
|
||||
A[/yggdrasil/sessionserver]
|
||||
A --> B[GET /session/minecraft/profile/:uuid]
|
||||
A --> C[POST /session/minecraft/join]
|
||||
A --> D[GET /session/minecraft/hasJoined]
|
||||
end
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [routes.go](file://internal/handler/routes.go#L100-L105)
|
||||
|
||||
**Section sources**
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L105)
|
||||
|
||||
## 核心API功能详解
|
||||
|
||||
### JoinServer API
|
||||
`JoinServer` API是建立服务器会话的核心。当Minecraft客户端成功登录后,会调用此API来“加入”一个特定的服务器。
|
||||
|
||||
**功能流程:**
|
||||
1. **接收参数**:API接收`serverId`、`accessToken`和`selectedProfile`(玩家UUID)三个必需参数。
|
||||
2. **输入验证**:对`serverId`进行格式检查(长度不超过100字符,不包含`<>\"'&`等危险字符),并验证客户端IP地址格式。
|
||||
3. **令牌验证**:通过`accessToken`在数据库中查找对应的令牌记录,确保令牌有效。
|
||||
4. **配置文件匹配**:将`selectedProfile`(客户端提供的UUID)与令牌中关联的`ProfileId`进行比对,确保玩家使用的是正确的配置文件。
|
||||
5. **构建会话数据**:从数据库中获取该UUID对应的玩家档案,提取其用户名(`Name`)。
|
||||
6. **存储会话**:将`accessToken`、`userName`、`selectedProfile`和客户端IP地址构建成`SessionData`结构体,序列化后存入Redis。存储的键名为`Join_` + `serverId`,TTL设置为15分钟。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "Minecraft客户端"
|
||||
participant Handler as "yggdrasil_handler"
|
||||
participant Service as "yggdrasil_service"
|
||||
participant Redis as "Redis"
|
||||
participant DB as "数据库"
|
||||
Client->>Handler : POST /join (serverId, accessToken, selectedProfile)
|
||||
Handler->>Service : JoinServer(serverId, accessToken, selectedProfile, clientIP)
|
||||
Service->>DB : 根据accessToken查找Token
|
||||
DB-->>Service : Token信息
|
||||
Service->>Service : 验证selectedProfile与Token匹配
|
||||
Service->>DB : 根据UUID查找Profile
|
||||
DB-->>Service : Profile (含用户名)
|
||||
Service->>Service : 构建SessionData对象
|
||||
Service->>Service : 序列化SessionData
|
||||
Service->>Redis : Set(Join_serverId, marshaledData, 15min)
|
||||
Redis-->>Service : 成功
|
||||
Service-->>Handler : 成功
|
||||
Handler-->>Client : HTTP 204 No Content
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L449-L496)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L163)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L449-L496)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L163)
|
||||
|
||||
### HasJoinedServer API
|
||||
`HasJoinedServer` API用于服务器端验证一个玩家是否已经通过了会话验证。
|
||||
|
||||
**功能流程:**
|
||||
1. **接收参数**:API接收`serverId`和`username`两个必需参数,以及可选的`ip`参数。
|
||||
2. **输入验证**:确保`serverId`和`username`不为空。
|
||||
3. **获取会话数据**:使用`Join_` + `serverId`作为键名,从Redis中获取之前存储的会话数据。
|
||||
4. **反序列化**:将获取到的JSON数据反序列化为`SessionData`结构体。
|
||||
5. **验证匹配**:
|
||||
- **用户名匹配**:比较会话数据中的`UserName`与请求中的`username`是否完全一致(区分大小写)。
|
||||
- **IP地址匹配**:如果请求中提供了`ip`参数,则会比较会话数据中的`IP`与请求的`ip`是否一致。
|
||||
6. **返回结果**:如果所有验证通过,则从数据库中获取该`username`对应的完整玩家档案(包括皮肤、披风等信息)并返回;否则返回验证失败。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Server as "Minecraft服务器"
|
||||
participant Handler as "yggdrasil_handler"
|
||||
participant Service as "yggdrasil_service"
|
||||
participant Redis as "Redis"
|
||||
participant DB as "数据库"
|
||||
Server->>Handler : GET /hasJoined?serverId=...&username=...&ip=...
|
||||
Handler->>Service : HasJoinedServer(serverId, username, ip)
|
||||
Service->>Redis : Get(Join_serverId)
|
||||
Redis-->>Service : marshaledData (或 nil)
|
||||
alt 会话不存在
|
||||
Service-->>Handler : 错误
|
||||
Handler-->>Server : HTTP 204 No Content
|
||||
else 会话存在
|
||||
Service->>Service : 反序列化为SessionData
|
||||
Service->>Service : 验证UserName == username
|
||||
Service->>Service : 验证IP (如果提供)
|
||||
alt 验证失败
|
||||
Service-->>Handler : 错误
|
||||
Handler-->>Server : HTTP 204 No Content
|
||||
else 验证成功
|
||||
Service->>DB : 根据username查找Profile
|
||||
DB-->>Service : Profile信息
|
||||
Service-->>Handler : Profile
|
||||
Handler-->>Server : HTTP 200 + Profile JSON
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L498-L552)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L165-L201)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L498-L552)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L165-L201)
|
||||
|
||||
### GetProfileByUUID API
|
||||
`GetProfileByUUID` API提供了一种通过玩家UUID查询其公开档案信息的方式。
|
||||
|
||||
**功能流程:**
|
||||
1. **接收参数**:从URL路径中获取`:uuid`参数。
|
||||
2. **格式化UUID**:调用`utils.FormatUUID`函数,将可能存在的十六进制格式UUID转换为标准的带连字符格式。
|
||||
3. **查询档案**:调用`service.GetProfileByUUID`方法,根据格式化后的UUID在数据库中查找对应的`Profile`记录。
|
||||
4. **序列化响应**:将`Profile`模型转换为包含皮肤(Skin)和披风(Cape)URL的`ProfileResponse`结构体。
|
||||
5. **返回结果**:将序列化后的JSON数据返回给客户端。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Handler as "yggdrasil_handler"
|
||||
participant Service as "profile_service"
|
||||
participant DB as "数据库"
|
||||
Client->>Handler : GET /profile/ : uuid
|
||||
Handler->>Handler : FormatUUID(uuid)
|
||||
Handler->>Service : GetProfileByUUID(uuid)
|
||||
Service->>DB : FindProfileByUUID(uuid)
|
||||
DB-->>Service : Profile
|
||||
Service-->>Handler : Profile
|
||||
Handler->>Handler : SerializeProfile(Profile)
|
||||
Handler-->>Client : HTTP 200 + Profile JSON
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L427-L447)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L427-L447)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L71-L81)
|
||||
|
||||
## 会话数据结构
|
||||
`SessionData`结构体定义了存储在Redis中的会话信息,是`JoinServer`和`HasJoinedServer`两个API之间通信的核心载体。
|
||||
|
||||
```go
|
||||
type SessionData struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
UserName string `json:"userName"`
|
||||
SelectedProfile string `json:"selectedProfile"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
```
|
||||
|
||||
- **AccessToken**:客户端的访问令牌,用于在`JoinServer`时验证身份。
|
||||
- **UserName**:玩家的用户名(如`Steve`),在`HasJoinedServer`时用于比对。
|
||||
- **SelectedProfile**:玩家的UUID,用于唯一标识玩家。
|
||||
- **IP**:客户端的IP地址,用于反作弊验证。
|
||||
|
||||
该结构体在`JoinServer`时被创建并序列化存储,在`HasJoinedServer`时被反序列化读取并用于验证。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class SessionData {
|
||||
+string accessToken
|
||||
+string userName
|
||||
+string selectedProfile
|
||||
+string ip
|
||||
}
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L25-L30)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L25-L30)
|
||||
|
||||
## 反作弊机制
|
||||
系统通过`HasJoinedServer` API实现了有效的反作弊机制,主要依赖于以下两个层面的验证:
|
||||
|
||||
1. **令牌绑定验证**:在`JoinServer`阶段,系统强制要求`selectedProfile`(UUID)必须与`accessToken`所关联的配置文件ID完全匹配。这确保了玩家不能使用他人的令牌来冒充身份。
|
||||
|
||||
2. **IP地址与时间戳验证**:
|
||||
- **IP地址验证**:`HasJoinedServer` API可以选择性地接收一个`ip`参数。如果提供了该参数,系统会将其与`JoinServer`时记录的IP地址进行比对。如果两者不一致,则验证失败。这可以有效防止玩家在一台机器上登录后,将令牌分享给另一台机器上的其他玩家使用。
|
||||
- **时间戳验证**:通过将Redis中会话数据的TTL设置为15分钟,系统实现了会话的自动过期。这意味着即使令牌和IP验证通过,该会话也只在有限时间内有效,增加了作弊的难度和成本。
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L195-L198)
|
||||
|
||||
## 与Minecraft客户端交互流程
|
||||
以下是Minecraft客户端与本会话服务交互的完整流程示例:
|
||||
|
||||
1. **客户端登录**:玩家在Minecraft启动器中输入邮箱和密码,启动器调用`/authserver/authenticate` API进行身份验证,并获取`accessToken`和`availableProfiles`。
|
||||
2. **选择配置文件**:启动器列出可用的配置文件,玩家选择一个(如`Steve`)。
|
||||
3. **加入服务器**:
|
||||
- 玩家在游戏内选择一个服务器并点击“加入”。
|
||||
- 启动器调用`/sessionserver/session/minecraft/join` API,携带`serverId`(服务器的哈希值)、`accessToken`和`selectedProfile`(`Steve`的UUID)。
|
||||
- 服务端验证信息无误后,将包含`accessToken`、`userName`(`Steve`)、`selectedProfile`和客户端IP的`SessionData`存入Redis,键名为`Join_` + `serverId`。
|
||||
4. **服务器验证**:
|
||||
- 游戏客户端连接到Minecraft服务器。
|
||||
- 服务器向本会话服务的`/sessionserver/session/minecraft/hasJoined` API发起请求,携带`serverId`、`username`(`Steve`)和客户端IP。
|
||||
- 服务端查找Redis中对应的会话数据,并验证`userName`和`ip`是否匹配。
|
||||
- 如果验证通过,服务端返回`Steve`的完整档案信息(包括皮肤URL),服务器允许玩家加入游戏;否则,连接被拒绝。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Minecraft客户端] --> |1. authenticate| B[/authserver/authenticate]
|
||||
B --> C{获取 accessToken 和 UUID}
|
||||
C --> D[选择配置文件]
|
||||
D --> E[点击加入服务器]
|
||||
E --> |3. join| F[/sessionserver/join]
|
||||
F --> G[Redis: 存储会话]
|
||||
E --> H[Minecraft服务器]
|
||||
H --> |4. hasJoined| I[/sessionserver/hasJoined]
|
||||
I --> J[Redis: 查找会话]
|
||||
J --> K{验证通过?}
|
||||
K --> |是| L[返回玩家档案]
|
||||
L --> M[允许加入游戏]
|
||||
K --> |否| N[拒绝连接]
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L449-L552)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L201)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L449-L552)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L201)
|
||||
|
||||
## 错误处理与日志
|
||||
系统在关键操作点都集成了详细的错误处理和日志记录:
|
||||
|
||||
- **输入验证**:对所有API的输入参数进行严格校验,如空值、格式错误等,并返回清晰的错误信息。
|
||||
- **业务逻辑错误**:对于令牌无效、配置文件不匹配、用户名不匹配等情况,返回特定的错误码和消息。
|
||||
- **系统错误**:对数据库查询失败、Redis操作失败、JSON序列化/反序列化失败等底层错误进行捕获和记录。
|
||||
- **日志记录**:使用`zap`日志库,对关键操作(如“玩家成功加入服务器”、“会话验证失败”)进行结构化日志记录,便于问题追踪和审计。
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L459-L484)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L103-L155)
|
||||
|
||||
## 测试验证
|
||||
系统的功能通过单元测试得到了充分验证,确保了核心逻辑的正确性。
|
||||
|
||||
- **常量验证**:测试确认`SessionKeyPrefix`常量值为`"Join_"`,`SessionTTL`为15分钟。
|
||||
- **输入验证**:对`JoinServer`和`HasJoinedServer`的输入参数(空值、格式)进行了全面的边界测试。
|
||||
- **逻辑验证**:测试了`JoinServer`的会话键生成逻辑,确保`serverId`能正确拼接成`Join_serverId`的格式。
|
||||
- **匹配逻辑**:验证了`HasJoinedServer`的用户名和IP地址匹配逻辑,确保大小写敏感和IP比对的正确性。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[测试用例] --> B[TestYggdrasilService_Constants]
|
||||
A --> C[TestJoinServer_InputValidation]
|
||||
A --> D[TestHasJoinedServer_InputValidation]
|
||||
A --> E[TestJoinServer_SessionKey]
|
||||
A --> F[TestHasJoinedServer_UsernameMatching]
|
||||
A --> G[TestHasJoinedServer_IPMatching]
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_service_test.go](file://internal/service/yggdrasil_service_test.go#L10-L350)
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_service_test.go](file://internal/service/yggdrasil_service_test.go#L10-L350)
|
||||
309
.qoder/repowiki/zh/content/API参考/Yggdrasil协议API/档案查询服务.md
Normal file
309
.qoder/repowiki/zh/content/API参考/Yggdrasil协议API/档案查询服务.md
Normal file
@@ -0,0 +1,309 @@
|
||||
# 档案查询服务
|
||||
|
||||
<cite>
|
||||
**本文档引用文件**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go)
|
||||
- [profile.go](file://internal/model/profile.go)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go)
|
||||
- [profile_service.go](file://internal/service/profile_service.go)
|
||||
- [yggdrasil_handler_test.go](file://internal/handler/yggdrasil_handler_test.go)
|
||||
- [texture.go](file://internal/model/texture.go)
|
||||
- [routes.go](file://internal/handler/routes.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [核心功能](#核心功能)
|
||||
3. [/api/profiles/minecraft 端点详解](#apiprofilesminecraft-端点详解)
|
||||
4. [/profile/:uuid 与 /api/profiles/minecraft 的区别](#profileuuid-与-apiprofilesminecraft-的区别)
|
||||
5. [皮肤与披风类型标识](#皮肤与披风类型标识)
|
||||
6. [请求与响应示例](#请求与响应示例)
|
||||
7. [应用场景](#应用场景)
|
||||
|
||||
## 简介
|
||||
|
||||
本服务为Minecraft Yggdrasil认证系统的一部分,提供档案(Profile)查询功能。核心功能包括根据用户名批量查询UUID和档案信息,以及根据UUID获取单个档案的完整详情。该服务支持Minecraft客户端在登录和聊天时加载玩家数据。
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L1-L667)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L1-L399)
|
||||
|
||||
## 核心功能
|
||||
|
||||
档案查询服务主要提供两种查询方式:
|
||||
1. 批量查询:通过用户名列表获取对应的UUID和基础档案信息
|
||||
2. 详情查询:通过UUID获取单个档案的完整信息,包括皮肤和披风等纹理数据
|
||||
|
||||
这些功能通过Yggdrasil API的特定端点实现,支持Minecraft客户端的数据加载需求。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[客户端请求] --> B{请求类型}
|
||||
B --> |批量查询| C[/api/profiles/minecraft]
|
||||
B --> |详情查询| D[/profile/:uuid]
|
||||
C --> E[返回UUID和基础信息]
|
||||
D --> F[返回完整档案信息]
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [routes.go](file://internal/handler/routes.go#L108-L109)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L164-L195)
|
||||
|
||||
## /api/profiles/minecraft 端点详解
|
||||
|
||||
`/api/profiles/minecraft` 端点用于根据玩家用户名列表批量查询对应的UUID和公开档案信息。
|
||||
|
||||
### 功能说明
|
||||
|
||||
该端点允许客户端一次性查询多个玩家的档案信息,主要用于:
|
||||
- 游戏登录时预加载玩家数据
|
||||
- 聊天系统中显示玩家名称和头像
|
||||
- 服务器列表中显示在线玩家信息
|
||||
|
||||
### 实现流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as 客户端
|
||||
participant Handler as yggdrasil_handler
|
||||
participant Service as profile_service
|
||||
participant Repository as profile_repository
|
||||
participant DB as 数据库
|
||||
Client->>Handler : POST /api/profiles/minecraft
|
||||
Handler->>Handler : 解析用户名数组
|
||||
Handler->>Service : GetProfilesDataByNames(names)
|
||||
Service->>Repository : GetProfilesByNames(names)
|
||||
Repository->>DB : SELECT * FROM profiles WHERE name IN (?)
|
||||
DB-->>Repository : 返回档案列表
|
||||
Repository-->>Service : 返回档案列表
|
||||
Service-->>Handler : 返回档案列表
|
||||
Handler-->>Client : 返回JSON响应
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L553-L587)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L237-L243)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L129-L137)
|
||||
|
||||
### 请求参数
|
||||
|
||||
- **方法**: POST
|
||||
- **路径**: `/api/profiles/minecraft`
|
||||
- **内容类型**: `application/json`
|
||||
- **请求体**: 包含用户名字符串数组
|
||||
|
||||
### 错误处理
|
||||
|
||||
该端点会处理以下错误情况:
|
||||
- 请求体格式无效
|
||||
- 用户名数组为空
|
||||
- 数据库查询失败
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L553-L587)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L237-L253)
|
||||
- [profile_repository.go](file://internal/repository/profile_repository.go#L129-L137)
|
||||
|
||||
## /profile/:uuid 与 /api/profiles/minecraft 的区别
|
||||
|
||||
这两个端点虽然都用于查询玩家档案,但功能和用途有明显区别:
|
||||
|
||||
### 功能对比
|
||||
|
||||
| 特性 | /profile/:uuid | /api/profiles/minecraft |
|
||||
|------|---------------|------------------------|
|
||||
| **查询方式** | 单个UUID查询 | 批量用户名查询 |
|
||||
| **主要用途** | 获取完整档案信息 | 批量映射用户名到UUID |
|
||||
| **返回数据** | 包含纹理数据的完整信息 | 基础档案信息 |
|
||||
| **使用场景** | 登录验证、档案详情展示 | 玩家列表、聊天显示 |
|
||||
|
||||
### 数据结构差异
|
||||
|
||||
`/profile/:uuid` 返回的数据包含完整的纹理信息:
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class ProfileResponse {
|
||||
+string uuid
|
||||
+string name
|
||||
+ProfileTexturesData textures
|
||||
+bool is_active
|
||||
+time.Time last_used_at
|
||||
+time.Time created_at
|
||||
}
|
||||
class ProfileTexturesData {
|
||||
+ProfileTexture SKIN
|
||||
+ProfileTexture CAPE
|
||||
}
|
||||
class ProfileTexture {
|
||||
+string url
|
||||
+ProfileTextureMetadata metadata
|
||||
}
|
||||
class ProfileTextureMetadata {
|
||||
+string model
|
||||
}
|
||||
ProfileResponse --> ProfileTexturesData : "包含"
|
||||
ProfileTexturesData --> ProfileTexture : "包含"
|
||||
ProfileTexture --> ProfileTextureMetadata : "包含"
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [profile.go](file://internal/model/profile.go#L31-L57)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L164-L195)
|
||||
|
||||
而 `/api/profiles/minecraft` 返回的是基础档案信息,主要用于名称到UUID的映射。
|
||||
|
||||
**Section sources**
|
||||
- [profile.go](file://internal/model/profile.go#L31-L57)
|
||||
- [profile_handler.go](file://internal/handler/profile_handler.go#L164-L195)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L553-L587)
|
||||
|
||||
## 皮肤与披风类型标识
|
||||
|
||||
在档案系统中,皮肤和披风的类型通过特定常量进行标识。
|
||||
|
||||
### 常量定义
|
||||
|
||||
根据 `yggdrasil_handler_test.go` 中的测试代码,皮肤和披风类型的常量定义如下:
|
||||
|
||||
```go
|
||||
const (
|
||||
TextureTypeSkin = "SKIN"
|
||||
TextureTypeCape = "CAPE"
|
||||
)
|
||||
```
|
||||
|
||||
这些常量在多个地方被使用和测试,确保系统的一致性。
|
||||
|
||||
### 测试验证
|
||||
|
||||
`yggdrasil_handler_test.go` 文件中的测试函数验证了这些常量的正确性:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始测试常量]) --> TestSkin["验证TextureTypeSkin = 'SKIN'"]
|
||||
TestSkin --> TestCape["验证TextureTypeCape = 'CAPE'"]
|
||||
TestCape --> CheckSkin["检查Skin常量值"]
|
||||
CheckSkin --> CheckCape["检查Cape常量值"]
|
||||
CheckCape --> End{测试结果}
|
||||
End --> |通过| Success["测试成功"]
|
||||
End --> |失败| Failure["测试失败"]
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler_test.go](file://internal/handler/yggdrasil_handler_test.go#L142-L156)
|
||||
- [texture.go](file://internal/model/texture.go#L10-L13)
|
||||
|
||||
### 数据模型
|
||||
|
||||
在 `texture.go` 文件中,这些类型被定义为枚举类型:
|
||||
|
||||
```go
|
||||
type TextureType string
|
||||
|
||||
const (
|
||||
TextureTypeSkin TextureType = "SKIN"
|
||||
TextureTypeCape TextureType = "CAPE"
|
||||
)
|
||||
```
|
||||
|
||||
这种设计确保了类型安全和代码可读性。
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler_test.go](file://internal/handler/yggdrasil_handler_test.go#L142-L156)
|
||||
- [texture.go](file://internal/model/texture.go#L8-L13)
|
||||
|
||||
## 请求与响应示例
|
||||
|
||||
### 请求示例
|
||||
|
||||
```json
|
||||
POST /api/profiles/minecraft
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
"Player1",
|
||||
"Player2",
|
||||
"Player3"
|
||||
]
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"uuid": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"userId": 1,
|
||||
"name": "Player1",
|
||||
"skinId": 1,
|
||||
"capeId": null,
|
||||
"isActive": true,
|
||||
"lastUsedAt": "2025-10-01T12:00:00Z",
|
||||
"createdAt": "2025-10-01T10:00:00Z",
|
||||
"updatedAt": "2025-10-01T10:00:00Z"
|
||||
},
|
||||
{
|
||||
"uuid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"userId": 2,
|
||||
"name": "Player2",
|
||||
"skinId": 2,
|
||||
"capeId": 3,
|
||||
"isActive": true,
|
||||
"lastUsedAt": "2025-10-01T11:00:00Z",
|
||||
"createdAt": "2025-09-30T09:00:00Z",
|
||||
"updatedAt": "2025-09-30T09:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
响应包含每个玩家的UUID、用户ID、名称、皮肤ID、披风ID等信息。
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L586)
|
||||
- [profile.go](file://internal/model/profile.go#L8-L24)
|
||||
|
||||
## 应用场景
|
||||
|
||||
### 游戏登录
|
||||
|
||||
当玩家登录游戏时,客户端会使用此服务来验证和获取玩家信息:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Minecraft客户端
|
||||
participant Auth as 认证服务器
|
||||
participant Profile as 档案服务
|
||||
Client->>Auth : 发送登录凭证
|
||||
Auth->>Profile : 查询玩家档案
|
||||
Profile->>Profile : 批量查询用户名对应的UUID
|
||||
Profile-->>Auth : 返回UUID和基础信息
|
||||
Auth->>Client : 返回登录结果和玩家信息
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L553-L587)
|
||||
- [routes.go](file://internal/handler/routes.go#L108-L109)
|
||||
|
||||
### 聊天显示
|
||||
|
||||
在聊天系统中,当玩家发送消息时,需要显示其名称和头像:
|
||||
|
||||
1. 客户端获取消息中的玩家用户名
|
||||
2. 调用 `/api/profiles/minecraft` 端点批量查询这些玩家的UUID
|
||||
3. 使用UUID获取玩家的皮肤信息并显示头像
|
||||
|
||||
### 服务器列表
|
||||
|
||||
在多人游戏服务器列表中,需要显示在线玩家的信息:
|
||||
|
||||
- 使用批量查询端点获取所有在线玩家的UUID
|
||||
- 根据UUID获取玩家的皮肤和披风信息
|
||||
- 在服务器列表中显示玩家头像和名称
|
||||
|
||||
这些应用场景都依赖于高效的批量查询能力,`/api/profiles/minecraft` 端点正是为此设计。
|
||||
|
||||
**Section sources**
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L553-L587)
|
||||
- [profile_service.go](file://internal/service/profile_service.go#L237-L243)
|
||||
464
.qoder/repowiki/zh/content/API参考/Yggdrasil协议API/认证服务.md
Normal file
464
.qoder/repowiki/zh/content/API参考/Yggdrasil协议API/认证服务.md
Normal file
@@ -0,0 +1,464 @@
|
||||
# 认证服务
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [routes.go](file://internal/handler/routes.go)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go)
|
||||
- [yggdrasil_service_test.go](file://internal/service/yggdrasil_service_test.go)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go)
|
||||
- [jwt.go](file://pkg/auth/jwt.go)
|
||||
- [redis.go](file://pkg/redis/redis.go)
|
||||
- [response.go](file://internal/model/response.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录](#附录)
|
||||
|
||||
## 简介
|
||||
本文件面向Yggdrasil认证服务的API文档,聚焦/authserver路由下的以下端点:
|
||||
- POST /authenticate:用户凭据认证,返回访问令牌与可用角色信息
|
||||
- POST /validate:校验访问令牌有效性
|
||||
- POST /refresh:在令牌过期或需要更新时刷新令牌
|
||||
- POST /invalidate:撤销单个访问令牌
|
||||
- POST /signout:基于邮箱+密码登出,撤销该用户所有令牌
|
||||
|
||||
文档将说明各端点的HTTP方法、请求体结构、响应数据格式、错误码含义,并结合会话数据存储机制(Redis)与TTL设置(15分钟),解释authenticate如何验证用户名/邮箱与密码、validate如何检查令牌有效性、refresh如何在令牌过期后重新生成新令牌。同时给出与内部用户系统的映射逻辑(如通过GetYggdrasilPasswordById查询密码)。
|
||||
|
||||
## 项目结构
|
||||
Yggdrasil认证服务位于路由组“/api/v1/yggdrasil/authserver”,由处理器模块负责接收请求、调用服务层完成业务逻辑,并通过Redis与数据库进行会话与令牌持久化。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "路由层"
|
||||
R["routes.go<br/>注册/authserver路由"]
|
||||
H["yggdrasil_handler.go<br/>认证处理器"]
|
||||
end
|
||||
subgraph "服务层"
|
||||
S["yggdrasil_service.go<br/>会话与加入服务器逻辑"]
|
||||
T["token_service.go<br/>令牌生成/验证/刷新/失效"]
|
||||
end
|
||||
subgraph "数据访问层"
|
||||
YR["yggdrasil_repository.go<br/>Yggdrasil密码查询"]
|
||||
end
|
||||
subgraph "基础设施"
|
||||
J["jwt.go<br/>JWT工具"]
|
||||
RC["redis.go<br/>Redis客户端"]
|
||||
RM["response.go<br/>通用响应结构"]
|
||||
end
|
||||
R --> H
|
||||
H --> S
|
||||
H --> T
|
||||
S --> YR
|
||||
T --> YR
|
||||
H --> J
|
||||
H --> RC
|
||||
H --> RM
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L425)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [jwt.go](file://pkg/auth/jwt.go#L1-L71)
|
||||
- [redis.go](file://pkg/redis/redis.go#L1-L175)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
|
||||
## 核心组件
|
||||
- 路由注册:在路由中将/authserver下的五个端点挂载到对应处理器函数。
|
||||
- 认证处理器:实现authenticate、validate、refresh、invalidate、signout等端点的请求解析、调用服务层、构造响应。
|
||||
- 令牌服务:封装令牌生成、验证、刷新、失效等逻辑,维护令牌表与清理策略。
|
||||
- 会话服务:封装JoinServer/HasJoinedServer,使用Redis存储会话数据,TTL为15分钟。
|
||||
- 数据访问:通过repository层访问数据库,如查询Yggdrasil密码。
|
||||
- 基础设施:JWT工具用于令牌签发与校验;Redis客户端用于会话数据持久化。
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L425)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [jwt.go](file://pkg/auth/jwt.go#L1-L71)
|
||||
- [redis.go](file://pkg/redis/redis.go#L1-L175)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
## 架构总览
|
||||
下图展示/authserver端点的请求-处理-响应流程,以及与服务层、Redis、数据库的关系。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "yggdrasil_handler.go"
|
||||
participant S as "yggdrasil_service.go"
|
||||
participant T as "token_service.go"
|
||||
participant YR as "yggdrasil_repository.go"
|
||||
participant DB as "数据库"
|
||||
participant RD as "Redis"
|
||||
rect rgb(255,255,255)
|
||||
Note over C,H : authenticate/validate/refresh/invalidate/signout
|
||||
end
|
||||
C->>H : POST /api/v1/yggdrasil/authserver/{endpoint}
|
||||
H->>DB : 读取/写入数据如用户、角色、令牌
|
||||
H->>RD : 读取/写入会话数据JoinServer/HasJoinedServer
|
||||
H->>T : 调用令牌相关服务生成/验证/刷新/失效
|
||||
H->>YR : 查询Yggdrasil密码凭据校验
|
||||
T->>DB : 操作token表创建/删除/查询
|
||||
S->>RD : Set/Get会话数据TTL=15分钟
|
||||
H-->>C : JSON响应含状态码与数据
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L425)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L202)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [redis.go](file://pkg/redis/redis.go#L60-L175)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 端点:POST /authenticate
|
||||
- 方法与路径
|
||||
- 方法:POST
|
||||
- 路径:/api/v1/yggdrasil/authserver/authenticate
|
||||
- 请求体结构
|
||||
- agent:对象,描述客户端信息(通常包含名称与版本)
|
||||
- clientToken:字符串,客户端令牌(可选)
|
||||
- identifier:字符串,用户名或邮箱(必填)
|
||||
- password:字符串,密码(必填)
|
||||
- requestUser:布尔,是否返回用户属性(可选)
|
||||
- 响应数据
|
||||
- accessToken:字符串,访问令牌
|
||||
- clientToken:字符串,客户端令牌
|
||||
- availableProfiles:数组,可用角色列表(每个元素为序列化后的角色信息)
|
||||
- selectedProfile:对象,当前选定的角色(可选)
|
||||
- user:对象,当requestUser=true时返回(包含id与properties)
|
||||
- 错误码
|
||||
- 400:请求参数错误
|
||||
- 403:用户名不存在或密码错误
|
||||
- 500:服务器内部错误
|
||||
- 处理流程要点
|
||||
- 根据identifier判断是邮箱还是用户名,分别查询用户或角色
|
||||
- 通过repository查询Yggdrasil密码并与请求密码比对
|
||||
- 生成新令牌(包含accessToken、clientToken),并返回可用角色列表
|
||||
- 如requestUser为true,附加用户属性
|
||||
- 与内部用户系统映射
|
||||
- 通过GetYggdrasilPasswordById查询Yggdrasil密码并与请求密码比对
|
||||
- 通过GetUserIDByEmail或GetProfileByProfileName获取用户ID
|
||||
- 通过GetProfileByUserId获取角色列表,自动选择唯一角色
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "Authenticate()"
|
||||
participant DB as "数据库"
|
||||
participant YR as "yggdrasil_repository.go"
|
||||
participant T as "token_service.go"
|
||||
C->>H : JSON请求identifier/password等
|
||||
H->>DB : 根据identifier定位用户/角色
|
||||
H->>YR : GetYggdrasilPasswordById(userId)
|
||||
YR-->>H : 返回Yggdrasil密码
|
||||
H->>H : 校验密码
|
||||
H->>T : NewToken(userId, UUID, clientToken)
|
||||
T-->>H : 返回accessToken/clientToken/availableProfiles
|
||||
H-->>C : 200 JSONaccessToken/clientToken/availableProfiles/selectedProfile/user
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L246)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L79)
|
||||
- [token_service.go](file://internal/service/token_service.go#L24-L79)
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L246)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L79)
|
||||
- [token_service.go](file://internal/service/token_service.go#L24-L79)
|
||||
|
||||
### 端点:POST /validate
|
||||
- 方法与路径
|
||||
- 方法:POST
|
||||
- 路径:/api/v1/yggdrasil/authserver/validate
|
||||
- 请求体结构
|
||||
- accessToken:字符串,访问令牌(必填)
|
||||
- clientToken:字符串,客户端令牌(可选)
|
||||
- 响应数据
|
||||
- 当令牌有效:204 No Content(无body)
|
||||
- 当令牌无效:403 Forbidden(valid=false)
|
||||
- 处理流程要点
|
||||
- 调用ValidToken校验accessToken与clientToken(若提供)
|
||||
- 有效返回204,无效返回403
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "ValidToken()"
|
||||
participant T as "token_service.go"
|
||||
C->>H : JSON请求accessToken, clientToken
|
||||
H->>T : ValidToken(accessToken, clientToken)
|
||||
alt 有效
|
||||
H-->>C : 204 No Content
|
||||
else 无效
|
||||
H-->>C : 403 {valid : false}
|
||||
end
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L248-L267)
|
||||
- [token_service.go](file://internal/service/token_service.go#L116-L141)
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L248-L267)
|
||||
- [token_service.go](file://internal/service/token_service.go#L116-L141)
|
||||
|
||||
### 端点:POST /refresh
|
||||
- 方法与路径
|
||||
- 方法:POST
|
||||
- 路径:/api/v1/yggdrasil/authserver/refresh
|
||||
- 请求体结构
|
||||
- accessToken:字符串,访问令牌(必填)
|
||||
- clientToken:字符串,客户端令牌(可选)
|
||||
- requestUser:布尔,是否返回用户属性(可选)
|
||||
- selectedProfile:对象,包含id字段(可选)
|
||||
- 响应数据
|
||||
- accessToken:字符串,新的访问令牌
|
||||
- clientToken:字符串,客户端令牌
|
||||
- selectedProfile:对象,选定的角色(可选)
|
||||
- user:对象,当requestUser=true时返回(包含id与properties)
|
||||
- 处理流程要点
|
||||
- 通过accessToken获取UUID与用户ID
|
||||
- 校验selectedProfile(若提供)与用户匹配
|
||||
- 调用RefreshToken生成新令牌,删除旧令牌
|
||||
- 返回新令牌与可选数据
|
||||
- 与内部用户系统映射
|
||||
- 通过GetUUIDByAccessToken与GetUserIDByAccessToken获取用户与角色信息
|
||||
- 通过ValidateProfileByUserID校验角色归属
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "RefreshToken()"
|
||||
participant T as "token_service.go"
|
||||
participant DB as "数据库"
|
||||
C->>H : JSON请求accessToken, clientToken, selectedProfile, requestUser
|
||||
H->>DB : GetUUIDByAccessToken / GetUserIDByAccessToken
|
||||
H->>H : 校验selectedProfile归属
|
||||
H->>T : RefreshToken(oldAccessToken, clientToken, selectedProfileID)
|
||||
T-->>H : 返回newAccessToken, clientToken
|
||||
H-->>C : 200 JSONnewAccessToken, clientToken, selectedProfile, user
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L269-L361)
|
||||
- [token_service.go](file://internal/service/token_service.go#L151-L238)
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L269-L361)
|
||||
- [token_service.go](file://internal/service/token_service.go#L151-L238)
|
||||
|
||||
### 端点:POST /invalidate
|
||||
- 方法与路径
|
||||
- 方法:POST
|
||||
- 路径:/api/v1/yggdrasil/authserver/invalidate
|
||||
- 请求体结构
|
||||
- accessToken:字符串,访问令牌(必填)
|
||||
- clientToken:字符串,客户端令牌(可选)
|
||||
- 响应数据
|
||||
- 204 No Content
|
||||
- 处理流程要点
|
||||
- 调用InvalidToken删除对应accessToken
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "InvalidToken()"
|
||||
participant T as "token_service.go"
|
||||
C->>H : JSON请求accessToken, clientToken
|
||||
H->>T : InvalidToken(accessToken)
|
||||
T-->>H : 删除完成
|
||||
H-->>C : 204 No Content
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L363-L378)
|
||||
- [token_service.go](file://internal/service/token_service.go#L240-L257)
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L363-L378)
|
||||
- [token_service.go](file://internal/service/token_service.go#L240-L257)
|
||||
|
||||
### 端点:POST /signout
|
||||
- 方法与路径
|
||||
- 方法:POST
|
||||
- 路径:/api/v1/yggdrasil/authserver/signout
|
||||
- 请求体结构
|
||||
- username:字符串,邮箱(必填)
|
||||
- password:字符串,密码(必填)
|
||||
- 响应数据
|
||||
- 204 No Content
|
||||
- 处理流程要点
|
||||
- 校验邮箱格式
|
||||
- 通过邮箱获取用户并查询Yggdrasil密码
|
||||
- 对比请求密码与存储密码
|
||||
- 调用InvalidUserTokens撤销该用户所有令牌
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "SignOut()"
|
||||
participant DB as "数据库"
|
||||
participant T as "token_service.go"
|
||||
C->>H : JSON请求username, password
|
||||
H->>H : 校验邮箱格式
|
||||
H->>DB : GetUserByEmail / GetYggdrasilPasswordById
|
||||
H->>H : 校验密码
|
||||
H->>T : InvalidUserTokens(userId)
|
||||
T-->>H : 删除完成
|
||||
H-->>C : 204 No Content
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L380-L425)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [token_service.go](file://internal/service/token_service.go#L259-L277)
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L380-L425)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [token_service.go](file://internal/service/token_service.go#L259-L277)
|
||||
|
||||
### 会话数据存储与TTL(JoinServer/HasJoinedServer)
|
||||
- 存储机制
|
||||
- 使用Redis存储玩家加入服务器的会话数据,键规则为“Join_”前缀+serverId
|
||||
- 数据结构包含accessToken、userName、selectedProfile、ip
|
||||
- TTL设置
|
||||
- 会话数据TTL为15分钟
|
||||
- 读写流程
|
||||
- JoinServer:校验参数与Token,获取角色信息,序列化会话数据并写入Redis
|
||||
- HasJoinedServer:从Redis读取会话数据,反序列化后校验用户名与IP(可选)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start(["进入JoinServer"]) --> Validate["校验serverId/accessToken/selectedProfile非空"]
|
||||
Validate --> Format["格式化UUID与IP校验"]
|
||||
Format --> GetToken["根据accessToken查询Token"]
|
||||
GetToken --> MatchProfile["selectedProfile与Token绑定Profile匹配"]
|
||||
MatchProfile --> LoadProfile["加载角色信息"]
|
||||
LoadProfile --> BuildData["构建SessionData结构"]
|
||||
BuildData --> Marshal["序列化SessionData"]
|
||||
Marshal --> Store["Redis Set(key='Join_serverId', value, TTL=15m)"]
|
||||
Store --> End(["完成"])
|
||||
subgraph "HasJoinedServer"
|
||||
HStart["读取serverId/username"] --> HLoad["Redis Get('Join_serverId')"]
|
||||
HLoad --> Parse["反序列化SessionData"]
|
||||
Parse --> CheckUser["校验userName匹配"]
|
||||
CheckUser --> CheckIP{"提供IP?"}
|
||||
CheckIP --> |是| IPMatch["校验IP匹配"]
|
||||
CheckIP --> |否| Success["通过"]
|
||||
IPMatch --> Success
|
||||
end
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L202)
|
||||
- [yggdrasil_service_test.go](file://internal/service/yggdrasil_service_test.go#L1-L351)
|
||||
- [redis.go](file://pkg/redis/redis.go#L60-L175)
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L19-L31)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L202)
|
||||
- [yggdrasil_service_test.go](file://internal/service/yggdrasil_service_test.go#L1-L351)
|
||||
- [redis.go](file://pkg/redis/redis.go#L60-L175)
|
||||
|
||||
## 依赖关系分析
|
||||
- 路由到处理器:/api/v1/yggdrasil/authserver/* 映射到yggdrasil_handler.go中的对应函数
|
||||
- 处理器到服务:认证处理器调用令牌服务与会话服务
|
||||
- 服务到仓库:令牌服务与会话服务通过repository层访问数据库
|
||||
- 会话服务到Redis:JoinServer/HasJoinedServer直接使用Redis客户端
|
||||
- 响应结构:统一使用通用响应结构(参考response.go)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Routes["routes.go"] --> Handler["yggdrasil_handler.go"]
|
||||
Handler --> TokenSvc["token_service.go"]
|
||||
Handler --> YggSvc["yggdrasil_service.go"]
|
||||
YggSvc --> Redis["redis.go"]
|
||||
TokenSvc --> Repo["yggdrasil_repository.go"]
|
||||
Handler --> Resp["response.go"]
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L425)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [redis.go](file://pkg/redis/redis.go#L1-L175)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
章节来源
|
||||
- [routes.go](file://internal/handler/routes.go#L87-L111)
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L425)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L1-L202)
|
||||
- [yggdrasil_repository.go](file://internal/repository/yggdrasil_repository.go#L1-L17)
|
||||
- [redis.go](file://pkg/redis/redis.go#L1-L175)
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
|
||||
## 性能考量
|
||||
- Redis读写:JoinServer/HasJoinedServer使用GetBytes/Set,TTL为15分钟,适合短生命周期的会话数据
|
||||
- 令牌清理:NewToken后异步触发CheckAndCleanupExcessTokens,限制用户最多保留10个令牌,降低数据库压力
|
||||
- 超时控制:服务层对数据库查询设置默认超时,避免阻塞
|
||||
- 并发安全:刷新令牌采用先创建新令牌再删除旧令牌的双写策略,减少事务复杂度
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L202)
|
||||
- [token_service.go](file://internal/service/token_service.go#L81-L114)
|
||||
- [redis.go](file://pkg/redis/redis.go#L60-L175)
|
||||
|
||||
## 故障排查指南
|
||||
- 400 Bad Request
|
||||
- 请求体解析失败或参数缺失
|
||||
- 会话加入/验证参数缺失(serverId/username)
|
||||
- 403 Forbidden
|
||||
- 认证失败(用户名不存在或密码错误)
|
||||
- 令牌无效或clientToken不匹配
|
||||
- 用户与角色不匹配
|
||||
- 500 Internal Server Error
|
||||
- 生成令牌失败、读取/写入Redis失败、数据库查询异常
|
||||
- 常见问题定位
|
||||
- 确认identifier是否为邮箱或用户名
|
||||
- 确认clientToken是否与accessToken匹配(validate/refresh)
|
||||
- 检查Redis连接与TTL设置(JoinServer/HasJoinedServer)
|
||||
- 检查用户角色与selectedProfile是否匹配(refresh)
|
||||
|
||||
章节来源
|
||||
- [yggdrasil_handler.go](file://internal/handler/yggdrasil_handler.go#L156-L425)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L81-L202)
|
||||
- [yggdrasil_service_test.go](file://internal/service/yggdrasil_service_test.go#L1-L351)
|
||||
|
||||
## 结论
|
||||
本认证服务围绕/authserver路由提供了完整的Yggdrasil认证能力,覆盖凭据认证、令牌验证、刷新、失效与登出。通过Redis实现15分钟TTL的会话数据存储,结合令牌服务的清理策略与严格的参数校验,确保系统在安全性与性能之间取得平衡。与内部用户系统的映射清晰,凭据校验通过getYggdrasilPasswordById完成,角色管理与令牌绑定完善。
|
||||
|
||||
## 附录
|
||||
- 错误码对照
|
||||
- 400:请求参数错误
|
||||
- 403:权限不足/令牌无效/用户不匹配
|
||||
- 500:服务器内部错误
|
||||
- 响应结构
|
||||
- 通用响应结构参考response.go中的Response/Error结构
|
||||
- 会话数据结构
|
||||
- SessionData包含accessToken、userName、selectedProfile、ip
|
||||
|
||||
章节来源
|
||||
- [response.go](file://internal/model/response.go#L1-L86)
|
||||
- [yggdrasil_service.go](file://internal/service/yggdrasil_service.go#L19-L31)
|
||||
Reference in New Issue
Block a user