// auth/middleware.go package auth import ( "crypto/subtle" "net/http" "time" ) type AuthMiddleware struct { username string password string sessions *SessionManager } func NewAuthMiddleware(username, password string, sessions *SessionManager) *AuthMiddleware { return &AuthMiddleware{ username: username, password: password, sessions: sessions, } } func (a *AuthMiddleware) Login(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { a.serveLoginPage(w) return } if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } username := r.FormValue("username") password := r.FormValue("password") // 使用常量时间比较防止时序攻击 usernameMatch := subtle.ConstantTimeCompare([]byte(username), []byte(a.username)) == 1 passwordMatch := subtle.ConstantTimeCompare([]byte(password), []byte(a.password)) == 1 if usernameMatch && passwordMatch { sessionID := a.sessions.Create(30 * time.Minute) http.SetCookie(w, &http.Cookie{ Name: "session_id", Value: sessionID, Path: "/", HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode, MaxAge: 1800, }) http.Redirect(w, r, "/", http.StatusSeeOther) return } // 登录失败,延迟响应防止暴力破解 time.Sleep(2 * time.Second) http.Redirect(w, r, "/login?error=1", http.StatusSeeOther) } func (a *AuthMiddleware) Logout(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_id") if err == nil { a.sessions.Delete(cookie.Value) } http.SetCookie(w, &http.Cookie{ Name: "session_id", Value: "", Path: "/", HttpOnly: true, Secure: true, MaxAge: -1, }) http.Redirect(w, r, "/login", http.StatusSeeOther) } func (a *AuthMiddleware) Require(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("session_id") if err != nil || !a.sessions.Valid(cookie.Value) { http.Redirect(w, r, "/login", http.StatusFound) return } next.ServeHTTP(w, r) }) } func (a *AuthMiddleware) serveLoginPage(w http.ResponseWriter) { html := `