feat(auth): 增强OAuth认证流程的错误处理和日志记录

- 添加详细的OAuth认证流程错误日志
- 增加对OAuth请求各个阶段的参数和状态验证
- 完善错误处理,提供更具体的错误信息和状态码
- 记录认证过程中的关键步骤和错误信息
- 新增客户端IP和请求来源的日志记录
- 优化OAuth令牌和用户信息的验证逻辑
This commit is contained in:
wood chen 2025-03-12 15:14:01 +08:00
parent 0ce0f75b58
commit 0d10e89a0b

View File

@ -173,51 +173,108 @@ func (h *ProxyHandler) OAuthCallbackHandler(w http.ResponseWriter, r *http.Reque
// 验证 state // 验证 state
if _, ok := h.auth.states.Load(state); !ok { 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) http.Error(w, "Invalid state", http.StatusBadRequest)
return return
} }
h.auth.states.Delete(state) 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) 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", resp, err := http.PostForm("https://connect.czl.net/api/oauth2/token",
url.Values{ url.Values{
"grant_type": {"authorization_code"},
"code": {code}, "code": {code},
"redirect_uri": {redirectURI}, "redirect_uri": {redirectURI},
"client_id": {clientID},
"client_secret": {clientSecret},
}) })
if err != nil { 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) http.Error(w, "Failed to get access token", http.StatusInternalServerError)
return return
} }
defer resp.Body.Close() 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 var token OAuthToken
if err := json.NewDecoder(resp.Body).Decode(&token); err != nil { 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) http.Error(w, "Failed to parse token response", http.StatusInternalServerError)
return 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, _ := http.NewRequest("GET", "https://connect.czl.net/api/oauth2/userinfo", nil)
req.Header.Set("Authorization", "Bearer "+token.AccessToken) req.Header.Set("Authorization", "Bearer "+token.AccessToken)
client := &http.Client{} client := &http.Client{Timeout: 10 * time.Second}
userResp, err := client.Do(req) userResp, err := client.Do(req)
if err != nil { 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) http.Error(w, "Failed to get user info", http.StatusInternalServerError)
return return
} }
defer userResp.Body.Close() 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 var userInfo OAuthUserInfo
if err := json.NewDecoder(userResp.Body).Decode(&userInfo); err != nil { 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) http.Error(w, "Failed to parse user info", http.StatusInternalServerError)
return 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() internalToken := h.auth.generateToken()
h.auth.addToken(internalToken, userInfo.Username, tokenExpiry) 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") w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, ` fmt.Fprintf(w, `