From a4067a6c66d7571b32baf12656d4c59084bd204f Mon Sep 17 00:00:00 2001 From: wood chen Date: Wed, 12 Mar 2025 15:43:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E5=A2=9E=E5=BC=BAOAuth=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BF=A1=E6=81=AF=E8=A7=A3=E6=9E=90=E5=92=8C=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构用户信息解析方法,支持更多JSON字段和灵活的用户名提取 - 添加调试日志记录用户信息响应内容 - 优化用户名提取策略,支持多种备用字段 - 增加头像URL的多字段兼容处理 - 改进用户信息验证和错误处理机制 - 扩展 OAuthUserInfo 结构体,支持更多可选字段 --- docker-compose.yml | 1 + internal/handler/auth.go | 78 +++++++++++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index da1d16d..391df1b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,4 +9,5 @@ services: environment: - TZ=Asia/Shanghai - OAUTH_CLIENT_ID=your_client_id + - OAUTH_CLIENT_SECRET=your_client_secret restart: always \ No newline at end of file diff --git a/internal/handler/auth.go b/internal/handler/auth.go index 84000b6..c0c8695 100644 --- a/internal/handler/auth.go +++ b/internal/handler/auth.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "io" "log" "net/http" "net/url" @@ -20,14 +21,23 @@ const ( ) type OAuthUserInfo struct { - ID string `json:"id"` - Email string `json:"email"` - Username string `json:"username"` - Name string `json:"name"` - AvatarURL string `json:"avatar_url"` - Admin bool `json:"admin"` - Moderator bool `json:"moderator"` - Groups []string `json:"groups"` + ID interface{} `json:"id,omitempty"` // 使用interface{}以接受数字或字符串 + Email string `json:"email,omitempty"` + Username string `json:"username,omitempty"` + Name string `json:"name,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` + Avatar string `json:"avatar,omitempty"` // 添加avatar字段 + Admin bool `json:"admin,omitempty"` + Moderator bool `json:"moderator,omitempty"` + Groups []string `json:"groups,omitempty"` + Upstreams interface{} `json:"upstreams,omitempty"` // 添加upstreams字段 + + // 添加可能的替代字段名 + Sub string `json:"sub,omitempty"` + PreferredName string `json:"preferred_username,omitempty"` + GivenName string `json:"given_name,omitempty"` + FamilyName string `json:"family_name,omitempty"` + Picture string `json:"picture,omitempty"` } type OAuthToken struct { @@ -255,17 +265,59 @@ func (h *ProxyHandler) OAuthCallbackHandler(w http.ResponseWriter, r *http.Reque return } - var userInfo OAuthUserInfo - if err := json.NewDecoder(userResp.Body).Decode(&userInfo); err != nil { - log.Printf("[Auth] ERR %s %s -> 500 (%s) failed to parse user info: %v from %s", r.Method, r.URL.Path, utils.GetClientIP(r), err, utils.GetRequestSource(r)) + // 读取响应体内容并记录 + bodyBytes, err := io.ReadAll(userResp.Body) + if err != nil { + log.Printf("[Auth] ERR %s %s -> 500 (%s) failed to read user info response body: %v from %s", + r.Method, r.URL.Path, utils.GetClientIP(r), err, utils.GetRequestSource(r)) + http.Error(w, "Failed to read user info response", http.StatusInternalServerError) + return + } + + // 记录响应内容(小心敏感信息) + log.Printf("[Auth] DEBUG %s %s -> user info response: %s", r.Method, r.URL.Path, string(bodyBytes)) + + // 使用更灵活的方式解析JSON + var rawUserInfo map[string]interface{} + if err := json.Unmarshal(bodyBytes, &rawUserInfo); err != nil { + log.Printf("[Auth] ERR %s %s -> 500 (%s) failed to parse raw user info: %v from %s", + r.Method, r.URL.Path, utils.GetClientIP(r), err, utils.GetRequestSource(r)) http.Error(w, "Failed to parse user info", http.StatusInternalServerError) return } + // 创建用户信息对象 + userInfo := OAuthUserInfo{} + + // 填充用户名(优先级:username > preferred_username > sub > email) + if username, ok := rawUserInfo["username"].(string); ok && username != "" { + userInfo.Username = username + } else if preferred, ok := rawUserInfo["preferred_username"].(string); ok && preferred != "" { + userInfo.Username = preferred + } else if sub, ok := rawUserInfo["sub"].(string); ok && sub != "" { + userInfo.Username = sub + } else if email, ok := rawUserInfo["email"].(string); ok && email != "" { + // 从邮箱中提取用户名 + parts := strings.Split(email, "@") + if len(parts) > 0 { + userInfo.Username = parts[0] + } + } + + // 填充头像URL + if avatar, ok := rawUserInfo["avatar"].(string); ok && avatar != "" { + userInfo.Avatar = avatar + } else if avatarURL, ok := rawUserInfo["avatar_url"].(string); ok && avatarURL != "" { + userInfo.AvatarURL = avatarURL + } else if picture, ok := rawUserInfo["picture"].(string); ok && picture != "" { + userInfo.Picture = picture + } + // 验证用户信息 if userInfo.Username == "" { - log.Printf("[Auth] ERR %s %s -> 500 (%s) received invalid user info (missing username) from %s", r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r)) - http.Error(w, "Invalid user information", http.StatusInternalServerError) + log.Printf("[Auth] ERR %s %s -> 500 (%s) could not extract username from user info from %s", + r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r)) + http.Error(w, "Invalid user information: missing username", http.StatusInternalServerError) return }