19 KiB
19 KiB
认证服务
**本文引用的文件** - [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)目录
简介
本文件面向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与数据库进行会话与令牌持久化。
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
- yggdrasil_handler.go
- yggdrasil_service.go
- yggdrasil_repository.go
- jwt.go
- redis.go
- response.go
章节来源
核心组件
- 路由注册:在路由中将/authserver下的五个端点挂载到对应处理器函数。
- 认证处理器:实现authenticate、validate、refresh、invalidate、signout等端点的请求解析、调用服务层、构造响应。
- 令牌服务:封装令牌生成、验证、刷新、失效等逻辑,维护令牌表与清理策略。
- 会话服务:封装JoinServer/HasJoinedServer,使用Redis存储会话数据,TTL为15分钟。
- 数据访问:通过repository层访问数据库,如查询Yggdrasil密码。
- 基础设施:JWT工具用于令牌签发与校验;Redis客户端用于会话数据持久化。
章节来源
- routes.go
- yggdrasil_handler.go
- yggdrasil_service.go
- yggdrasil_repository.go
- jwt.go
- redis.go
- response.go
架构总览
下图展示/authserver端点的请求-处理-响应流程,以及与服务层、Redis、数据库的关系。
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响应含状态码与数据
图表来源
详细组件分析
端点: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获取角色列表,自动选择唯一角色
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
图表来源
章节来源
端点:POST /validate
- 方法与路径
- 方法:POST
- 路径:/api/v1/yggdrasil/authserver/validate
- 请求体结构
- accessToken:字符串,访问令牌(必填)
- clientToken:字符串,客户端令牌(可选)
- 响应数据
- 当令牌有效:204 No Content(无body)
- 当令牌无效:403 Forbidden(valid=false)
- 处理流程要点
- 调用ValidToken校验accessToken与clientToken(若提供)
- 有效返回204,无效返回403
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
图表来源
章节来源
端点: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校验角色归属
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
图表来源
章节来源
端点:POST /invalidate
- 方法与路径
- 方法:POST
- 路径:/api/v1/yggdrasil/authserver/invalidate
- 请求体结构
- accessToken:字符串,访问令牌(必填)
- clientToken:字符串,客户端令牌(可选)
- 响应数据
- 204 No Content
- 处理流程要点
- 调用InvalidToken删除对应accessToken
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
图表来源
章节来源
端点:POST /signout
- 方法与路径
- 方法:POST
- 路径:/api/v1/yggdrasil/authserver/signout
- 请求体结构
- username:字符串,邮箱(必填)
- password:字符串,密码(必填)
- 响应数据
- 204 No Content
- 处理流程要点
- 校验邮箱格式
- 通过邮箱获取用户并查询Yggdrasil密码
- 对比请求密码与存储密码
- 调用InvalidUserTokens撤销该用户所有令牌
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
图表来源
章节来源
会话数据存储与TTL(JoinServer/HasJoinedServer)
- 存储机制
- 使用Redis存储玩家加入服务器的会话数据,键规则为“Join_”前缀+serverId
- 数据结构包含accessToken、userName、selectedProfile、ip
- TTL设置
- 会话数据TTL为15分钟
- 读写流程
- JoinServer:校验参数与Token,获取角色信息,序列化会话数据并写入Redis
- HasJoinedServer:从Redis读取会话数据,反序列化后校验用户名与IP(可选)
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
图表来源
章节来源
依赖关系分析
- 路由到处理器:/api/v1/yggdrasil/authserver/* 映射到yggdrasil_handler.go中的对应函数
- 处理器到服务:认证处理器调用令牌服务与会话服务
- 服务到仓库:令牌服务与会话服务通过repository层访问数据库
- 会话服务到Redis:JoinServer/HasJoinedServer直接使用Redis客户端
- 响应结构:统一使用通用响应结构(参考response.go)
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"]
图表来源
章节来源
性能考量
- Redis读写:JoinServer/HasJoinedServer使用GetBytes/Set,TTL为15分钟,适合短生命周期的会话数据
- 令牌清理:NewToken后异步触发CheckAndCleanupExcessTokens,限制用户最多保留10个令牌,降低数据库压力
- 超时控制:服务层对数据库查询设置默认超时,避免阻塞
- 并发安全:刷新令牌采用先创建新令牌再删除旧令牌的双写策略,减少事务复杂度
章节来源
故障排查指南
- 400 Bad Request
- 请求体解析失败或参数缺失
- 会话加入/验证参数缺失(serverId/username)
- 403 Forbidden
- 认证失败(用户名不存在或密码错误)
- 令牌无效或clientToken不匹配
- 用户与角色不匹配
- 500 Internal Server Error
- 生成令牌失败、读取/写入Redis失败、数据库查询异常
- 常见问题定位
- 确认identifier是否为邮箱或用户名
- 确认clientToken是否与accessToken匹配(validate/refresh)
- 检查Redis连接与TTL设置(JoinServer/HasJoinedServer)
- 检查用户角色与selectedProfile是否匹配(refresh)
章节来源
结论
本认证服务围绕/authserver路由提供了完整的Yggdrasil认证能力,覆盖凭据认证、令牌验证、刷新、失效与登出。通过Redis实现15分钟TTL的会话数据存储,结合令牌服务的清理策略与严格的参数校验,确保系统在安全性与性能之间取得平衡。与内部用户系统的映射清晰,凭据校验通过getYggdrasilPasswordById完成,角色管理与令牌绑定完善。
附录
- 错误码对照
- 400:请求参数错误
- 403:权限不足/令牌无效/用户不匹配
- 500:服务器内部错误
- 响应结构
- 通用响应结构参考response.go中的Response/Error结构
- 会话数据结构
- SessionData包含accessToken、userName、selectedProfile、ip
章节来源