forked from datawhale/whale-town-front
feat:实现登录系统和用户认证功能
- 添加登录场景(login_scene.tscn)和主菜单场景(main_menu_scene.tscn) - 实现认证管理器(AuthManager)用于用户登录和会话管理 - 添加核心服务:加密服务、存储服务、网络服务 - 配置项目主场景为登录场景 - 添加自动加载服务到项目配置 - 添加开发环境配置(VSCode、Claude) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
183
scripts/managers/AuthManager.gd
Normal file
183
scripts/managers/AuthManager.gd
Normal file
@@ -0,0 +1,183 @@
|
||||
extends Node
|
||||
|
||||
## 认证管理器
|
||||
## 统一管理用户登录、登出、Token等认证相关功能
|
||||
|
||||
# 登录状态枚举
|
||||
enum LoginState {
|
||||
NOT_LOGGED_IN, # 未登录
|
||||
LOGGING_IN, # 登录中
|
||||
LOGGED_IN, # 已登录
|
||||
GUEST # 游客模式
|
||||
}
|
||||
|
||||
# 信号
|
||||
signal login_success(userData: UserData)
|
||||
signal login_failed(errorCode: int, errorMessage: String)
|
||||
signal logout_success()
|
||||
|
||||
# 当前登录状态
|
||||
var currentState: LoginState = LoginState.NOT_LOGGED_IN
|
||||
|
||||
# Token
|
||||
var accessToken: String = ""
|
||||
var tokenExpireTime: int = 0
|
||||
|
||||
# 当前用户信息
|
||||
var currentUser: UserData = null
|
||||
|
||||
# 是否正在登录(防止重复请求)
|
||||
var _isLoggingIn: bool = false
|
||||
|
||||
func _ready():
|
||||
# 游戏启动时尝试从本地加载 Token 和用户信息
|
||||
_tryLoadLocalAuth()
|
||||
|
||||
## 登录
|
||||
func login(username: String, password: String, rememberPassword: bool) -> Dictionary:
|
||||
# 防止重复登录
|
||||
if _isLoggingIn:
|
||||
return {
|
||||
"success": false,
|
||||
"error_code": ErrorCode.INVALID_INPUT,
|
||||
"message": "登录中,请稍候..."
|
||||
}
|
||||
|
||||
_isLoggingIn = true
|
||||
currentState = LoginState.LOGGING_IN
|
||||
|
||||
# 构建登录请求数据
|
||||
var requestData = {
|
||||
"identifier": username,
|
||||
"password": password
|
||||
}
|
||||
|
||||
# 发送登录请求
|
||||
var response = await NetworkService.post("/api/auth/login", requestData)
|
||||
|
||||
_isLoggingIn = false
|
||||
|
||||
# 处理响应
|
||||
if response.success:
|
||||
# 解析用户数据和 Token
|
||||
var data = response.get("data", {})
|
||||
var token = data.get("access_token", "")
|
||||
var userData = data.get("user", {})
|
||||
|
||||
if token.is_empty():
|
||||
var errorMsg = "服务器返回数据格式错误"
|
||||
login_failed.emit(ErrorCode.SERVER_ERROR, errorMsg)
|
||||
currentState = LoginState.NOT_LOGGED_IN
|
||||
return {
|
||||
"success": false,
|
||||
"error_code": ErrorCode.SERVER_ERROR,
|
||||
"message": errorMsg
|
||||
}
|
||||
|
||||
# 保存 Token
|
||||
accessToken = token
|
||||
tokenExpireTime = int(Time.get_unix_time_from_system()) + 3600 * 24 # 假设24小时过期
|
||||
StorageService.saveToken(accessToken, tokenExpireTime)
|
||||
|
||||
# 保存用户信息
|
||||
currentUser = UserData.fromDict(userData)
|
||||
StorageService.saveUserData(currentUser)
|
||||
|
||||
# 如果勾选记住密码,保存账号密码
|
||||
if rememberPassword:
|
||||
StorageService.saveAccount(username, password, true)
|
||||
else:
|
||||
# 只保存用户名,不保存密码
|
||||
StorageService.save("account", "last_username", username)
|
||||
StorageService.clearSavedPassword()
|
||||
|
||||
# 更新状态
|
||||
currentState = LoginState.LOGGED_IN
|
||||
|
||||
# 发送登录成功信号
|
||||
login_success.emit(currentUser)
|
||||
|
||||
return {
|
||||
"success": true,
|
||||
"message": "登录成功",
|
||||
"user": currentUser
|
||||
}
|
||||
else:
|
||||
# 登录失败
|
||||
var errorCode = response.get("error_code", ErrorCode.WRONG_CREDENTIALS)
|
||||
var errorMsg = response.get("message", "登录失败")
|
||||
|
||||
currentState = LoginState.NOT_LOGGED_IN
|
||||
login_failed.emit(errorCode, errorMsg)
|
||||
|
||||
return {
|
||||
"success": false,
|
||||
"error_code": errorCode,
|
||||
"message": errorMsg
|
||||
}
|
||||
|
||||
## 登出
|
||||
func logout() -> void:
|
||||
# 清除 Token 和用户信息
|
||||
accessToken = ""
|
||||
tokenExpireTime = 0
|
||||
currentUser = null
|
||||
|
||||
# 清除本地存储(保留用户名和记住密码设置)
|
||||
StorageService.clearToken()
|
||||
|
||||
# 更新状态
|
||||
currentState = LoginState.NOT_LOGGED_IN
|
||||
|
||||
# 发送登出成功信号
|
||||
logout_success.emit()
|
||||
|
||||
print("用户已登出")
|
||||
|
||||
## 检查是否已登录
|
||||
func isLoggedIn() -> bool:
|
||||
return currentState == LoginState.LOGGED_IN and currentUser != null
|
||||
|
||||
## 获取当前用户
|
||||
func getCurrentUser() -> UserData:
|
||||
return currentUser
|
||||
|
||||
## 获取当前 Token
|
||||
func getToken() -> String:
|
||||
return accessToken
|
||||
|
||||
## 尝试从本地加载认证信息
|
||||
func _tryLoadLocalAuth() -> void:
|
||||
# 加载 Token
|
||||
var tokenData = StorageService.loadToken()
|
||||
var token = tokenData.get("token", "")
|
||||
var expireTime = tokenData.get("expire_time", 0)
|
||||
|
||||
# 检查 Token 是否有效
|
||||
var currentTime = int(Time.get_unix_time_from_system())
|
||||
if token.is_empty() or expireTime <= currentTime:
|
||||
print("本地 Token 无效或已过期")
|
||||
return
|
||||
|
||||
# Token 有效,加载用户信息
|
||||
var userData = StorageService.loadUserData()
|
||||
if userData == null:
|
||||
print("本地用户信息不存在")
|
||||
return
|
||||
|
||||
# 恢复登录状态
|
||||
accessToken = token
|
||||
tokenExpireTime = expireTime
|
||||
currentUser = userData
|
||||
currentState = LoginState.LOGGED_IN
|
||||
|
||||
print("从本地恢复登录状态: ", currentUser.username)
|
||||
|
||||
# 可选:静默刷新 Token(如果接近过期)
|
||||
# _refreshTokenIfNeeded()
|
||||
|
||||
## 检查 Token 是否即将过期
|
||||
func isTokenExpiring() -> bool:
|
||||
var currentTime = int(Time.get_unix_time_from_system())
|
||||
var timeLeft = tokenExpireTime - currentTime
|
||||
return timeLeft < 300 # 少于5分钟
|
||||
Reference in New Issue
Block a user