Files
gemini-banlancer/internal/webhandlers/auth_handler.go

119 lines
2.8 KiB
Go

// Filename: internal/webhandlers/auth_handler.go
package webhandlers
import (
"gemini-balancer/internal/middleware"
"gemini-balancer/internal/service"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// WebAuthHandler Web 认证处理器
type WebAuthHandler struct {
securityService *service.SecurityService
logger *logrus.Logger
}
// NewWebAuthHandler 创建 WebAuthHandler
func NewWebAuthHandler(securityService *service.SecurityService) *WebAuthHandler {
logger := logrus.New()
logger.SetLevel(logrus.InfoLevel)
return &WebAuthHandler{
securityService: securityService,
logger: logger,
}
}
// ShowLoginPage 显示登录页面
func (h *WebAuthHandler) ShowLoginPage(c *gin.Context) {
errMsg := c.Query("error")
// 验证重定向路径(防止开放重定向攻击)
redirectPath := h.validateRedirectPath(c.Query("redirect"))
// 如果已登录,直接重定向
if cookie := middleware.ExtractTokenFromCookie(c); cookie != "" {
if _, err := h.securityService.AuthenticateToken(cookie); err == nil {
c.Redirect(http.StatusFound, redirectPath)
return
}
}
c.HTML(http.StatusOK, "auth.html", gin.H{
"error": errMsg,
"redirect": redirectPath,
})
}
// HandleLogin 已废弃(项目无用户名系统)
func (h *WebAuthHandler) HandleLogin(c *gin.Context) {
c.Redirect(http.StatusFound, "/login?error=DEPRECATED_LOGIN_METHOD")
}
// HandleLogout 处理登出请求
func (h *WebAuthHandler) HandleLogout(c *gin.Context) {
cookie := middleware.ExtractTokenFromCookie(c)
if cookie != "" {
// 尝试获取 Token 信息用于日志
authToken, err := h.securityService.AuthenticateToken(cookie)
if err == nil {
h.logger.WithFields(logrus.Fields{
"token_id": authToken.ID,
"client_ip": c.ClientIP(),
}).Info("User logged out")
} else {
h.logger.WithField("client_ip", c.ClientIP()).Warn("Logout with invalid token")
}
// 使缓存失效
middleware.InvalidateTokenCache(cookie)
} else {
h.logger.WithField("client_ip", c.ClientIP()).Debug("Logout without session cookie")
}
// 清除 Cookie
middleware.ClearAdminSessionCookie(c)
// 重定向到登录页
c.Redirect(http.StatusFound, "/login")
}
// validateRedirectPath 验证重定向路径(防止开放重定向攻击)
func (h *WebAuthHandler) validateRedirectPath(path string) string {
defaultPath := "/dashboard"
if path == "" {
return defaultPath
}
// 只允许内部路径
if !strings.HasPrefix(path, "/") || strings.HasPrefix(path, "//") {
h.logger.WithField("path", path).Warn("Invalid redirect path blocked")
return defaultPath
}
// 白名单验证
allowedPaths := []string{
"/dashboard",
"/keys",
"/settings",
"/logs",
"/tasks",
"/chat",
}
for _, allowed := range allowedPaths {
if strings.HasPrefix(path, allowed) {
return path
}
}
return defaultPath
}