119 lines
2.8 KiB
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
|
|
}
|