From 0d10e89a0bf5c36783a49b912b8cc0bfbd21545f Mon Sep 17 00:00:00 2001 From: wood chen Date: Wed, 12 Mar 2025 15:14:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E5=A2=9E=E5=BC=BAOAuth=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E6=B5=81=E7=A8=8B=E7=9A=84=E9=94=99=E8=AF=AF=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=92=8C=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加详细的OAuth认证流程错误日志 - 增加对OAuth请求各个阶段的参数和状态验证 - 完善错误处理,提供更具体的错误信息和状态码 - 记录认证过程中的关键步骤和错误信息 - 新增客户端IP和请求来源的日志记录 - 优化OAuth令牌和用户信息的验证逻辑 --- internal/handler/auth.go | 63 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/internal/handler/auth.go b/internal/handler/auth.go index aaa6732..84000b6 100644 --- a/internal/handler/auth.go +++ b/internal/handler/auth.go @@ -173,51 +173,108 @@ func (h *ProxyHandler) OAuthCallbackHandler(w http.ResponseWriter, r *http.Reque // 验证 state if _, ok := h.auth.states.Load(state); !ok { + log.Printf("[Auth] ERR %s %s -> 400 (%s) invalid state from %s", r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r)) http.Error(w, "Invalid state", http.StatusBadRequest) return } h.auth.states.Delete(state) + // 验证code参数 + if code == "" { + log.Printf("[Auth] ERR %s %s -> 400 (%s) missing code parameter from %s", r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r)) + http.Error(w, "Missing code parameter", http.StatusBadRequest) + return + } + // 获取访问令牌 redirectURI := getCallbackURL(r) + clientID := os.Getenv("OAUTH_CLIENT_ID") + clientSecret := os.Getenv("OAUTH_CLIENT_SECRET") + + // 验证OAuth配置 + if clientID == "" || clientSecret == "" { + log.Printf("[Auth] ERR %s %s -> 500 (%s) missing OAuth credentials from %s", r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r)) + http.Error(w, "Server configuration error", http.StatusInternalServerError) + return + } + resp, err := http.PostForm("https://connect.czl.net/api/oauth2/token", url.Values{ - "code": {code}, - "redirect_uri": {redirectURI}, + "grant_type": {"authorization_code"}, + "code": {code}, + "redirect_uri": {redirectURI}, + "client_id": {clientID}, + "client_secret": {clientSecret}, }) if err != nil { + log.Printf("[Auth] ERR %s %s -> 500 (%s) failed to get access token: %v from %s", r.Method, r.URL.Path, utils.GetClientIP(r), err, utils.GetRequestSource(r)) http.Error(w, "Failed to get access token", http.StatusInternalServerError) return } defer resp.Body.Close() + // 检查响应状态码 + if resp.StatusCode != http.StatusOK { + log.Printf("[Auth] ERR %s %s -> %d (%s) OAuth server returned error status: %s from %s", + r.Method, r.URL.Path, resp.StatusCode, utils.GetClientIP(r), resp.Status, utils.GetRequestSource(r)) + http.Error(w, "OAuth server error: "+resp.Status, http.StatusInternalServerError) + return + } + var token OAuthToken if err := json.NewDecoder(resp.Body).Decode(&token); err != nil { + log.Printf("[Auth] ERR %s %s -> 500 (%s) failed to parse token response: %v from %s", r.Method, r.URL.Path, utils.GetClientIP(r), err, utils.GetRequestSource(r)) http.Error(w, "Failed to parse token response", http.StatusInternalServerError) return } + // 验证访问令牌 + if token.AccessToken == "" { + log.Printf("[Auth] ERR %s %s -> 500 (%s) received empty access token from %s", r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r)) + http.Error(w, "Received invalid token", http.StatusInternalServerError) + return + } + // 获取用户信息 req, _ := http.NewRequest("GET", "https://connect.czl.net/api/oauth2/userinfo", nil) req.Header.Set("Authorization", "Bearer "+token.AccessToken) - client := &http.Client{} + client := &http.Client{Timeout: 10 * time.Second} userResp, err := client.Do(req) if err != nil { + log.Printf("[Auth] ERR %s %s -> 500 (%s) failed to get user info: %v from %s", r.Method, r.URL.Path, utils.GetClientIP(r), err, utils.GetRequestSource(r)) http.Error(w, "Failed to get user info", http.StatusInternalServerError) return } defer userResp.Body.Close() + // 检查用户信息响应状态码 + if userResp.StatusCode != http.StatusOK { + log.Printf("[Auth] ERR %s %s -> %d (%s) userinfo endpoint returned error status: %s from %s", + r.Method, r.URL.Path, userResp.StatusCode, utils.GetClientIP(r), userResp.Status, utils.GetRequestSource(r)) + http.Error(w, "Failed to get user info: "+userResp.Status, http.StatusInternalServerError) + 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)) http.Error(w, "Failed to parse user info", http.StatusInternalServerError) return } + // 验证用户信息 + 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) + return + } + // 生成内部访问令牌 internalToken := h.auth.generateToken() h.auth.addToken(internalToken, userInfo.Username, tokenExpiry) + log.Printf("[Auth] %s %s -> 200 (%s) login success for user %s from %s", r.Method, r.URL.Path, utils.GetClientIP(r), userInfo.Username, utils.GetRequestSource(r)) + // 返回登录成功页面 w.Header().Set("Content-Type", "text/html") fmt.Fprintf(w, `