feat: add admin login page with session auth
Build and Push to GHCR / build-and-push (push) Has been cancelled
Build and Push to GHCR / build-and-push (push) Has been cancelled
Replace HTTP Basic Auth with a dedicated login page using cookie-based session authentication. - Add web/static/login.html with form-based login UI - Add in-memory session store with configurable timeout - Add /api/auth/login, /api/auth/logout, /api/auth/me endpoints - Replace BasicAuth middleware with SessionAuth - Add session_timeout config field (default 24h) - Remove listen_addr from settings UI via [HAPI](https://hapi.run) Co-Authored-By: HAPI <[email protected]>
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"mirror-proxy/internal/admin"
|
||||
)
|
||||
|
||||
// CORS 跨域中间件
|
||||
@@ -45,21 +47,42 @@ func (rw *responseWriter) WriteHeader(code int) {
|
||||
rw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
// BasicAuth 基础认证中间件
|
||||
func BasicAuth(username, password string) func(http.Handler) http.Handler {
|
||||
// SessionAuth 基于 Cookie Session 的认证中间件
|
||||
func SessionAuth(sessions *admin.SessionStore, loginPath string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
if !ok || user != username || pass != password {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="Admin Panel"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
cookie, err := r.Cookie("session")
|
||||
if err != nil {
|
||||
unauthorized(w, r, loginPath)
|
||||
return
|
||||
}
|
||||
|
||||
_, ok := sessions.Get(cookie.Value)
|
||||
if !ok {
|
||||
unauthorized(w, r, loginPath)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func unauthorized(w http.ResponseWriter, r *http.Request, loginPath string) {
|
||||
accept := r.Header.Get("Accept")
|
||||
isAPI := strings.Contains(r.URL.Path, "/api/") ||
|
||||
strings.Contains(accept, "application/json")
|
||||
|
||||
if isAPI {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(`{"error":"unauthorized"}`))
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, loginPath, http.StatusFound)
|
||||
}
|
||||
|
||||
// StripPrefix 安全地移除路径前缀
|
||||
func StripPrefix(prefix string, h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user