first commit
This commit is contained in:
100
middleware/auth.go
Normal file
100
middleware/auth.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"stocksearch/services"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const SessionCookieName = "ss_session"
|
||||
|
||||
// contextKey 컨텍스트 키 타입
|
||||
type contextKey string
|
||||
|
||||
const CtxLoggedIn contextKey = "loggedIn"
|
||||
|
||||
// IsLoggedIn 요청 컨텍스트에서 로그인 여부 반환
|
||||
func IsLoggedIn(r *http.Request) bool {
|
||||
v, _ := r.Context().Value(CtxLoggedIn).(bool)
|
||||
return v
|
||||
}
|
||||
|
||||
// publicPaths 로그인 없이 접근 가능한 경로 (전체 일치 또는 prefix)
|
||||
var publicPaths = []string{
|
||||
"/login",
|
||||
"/static/",
|
||||
// 공개 페이지
|
||||
"/",
|
||||
"/theme",
|
||||
"/kospi200",
|
||||
"/stock/",
|
||||
// 공개 API (시세/테마/코스피200 관련)
|
||||
"/api/stock/",
|
||||
"/api/indices",
|
||||
"/api/search",
|
||||
"/api/scanner/",
|
||||
"/api/signal",
|
||||
"/api/watchlist-signal",
|
||||
"/api/themes",
|
||||
"/api/themes/",
|
||||
"/api/kospi200",
|
||||
"/api/news",
|
||||
"/api/disclosure",
|
||||
"/ws",
|
||||
}
|
||||
|
||||
// isPublic 요청 경로가 공개 경로에 해당하는지 판단
|
||||
func isPublic(path string) bool {
|
||||
for _, p := range publicPaths {
|
||||
if strings.HasSuffix(p, "/") {
|
||||
if strings.HasPrefix(path, p) || path == p[:len(p)-1] {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if path == p {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Auth 세션 쿠키 검증 미들웨어
|
||||
// - 공개 경로: 로그인 없이 접근 허용
|
||||
// - 인증 필요 경로(자산/주문/자동매매 등): 미인증 시 페이지는 /login 리다이렉트, API는 401 반환
|
||||
func Auth(sessionSvc *services.SessionService) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 공개 경로: 인증 없이 통과하되 로그인 상태는 컨텍스트에 저장
|
||||
if isPublic(r.URL.Path) {
|
||||
cookie, err := r.Cookie(SessionCookieName)
|
||||
loggedIn := err == nil && sessionSvc.Validate(cookie.Value)
|
||||
ctx := context.WithValue(r.Context(), CtxLoggedIn, loggedIn)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
return
|
||||
}
|
||||
|
||||
// 쿠키에서 세션 ID 조회
|
||||
cookie, err := r.Cookie(SessionCookieName)
|
||||
loggedIn := err == nil && sessionSvc.Validate(cookie.Value)
|
||||
|
||||
if !loggedIn {
|
||||
// API 경로는 401 JSON 반환, 페이지는 /login 리다이렉트
|
||||
if strings.HasPrefix(r.URL.Path, "/api/") {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
_, _ = w.Write([]byte(`{"error":"로그인이 필요합니다"}`))
|
||||
return
|
||||
}
|
||||
redirectURL := "/login?next=" + r.URL.RequestURI()
|
||||
http.Redirect(w, r, redirectURL, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 로그인 상태를 컨텍스트에 저장
|
||||
ctx := context.WithValue(r.Context(), CtxLoggedIn, true)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
37
middleware/middleware.go
Normal file
37
middleware/middleware.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Logger HTTP 요청 로깅 미들웨어
|
||||
func Logger(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
next.ServeHTTP(w, r)
|
||||
log.Printf("%s %s %s", r.Method, r.RequestURI, time.Since(start))
|
||||
})
|
||||
}
|
||||
|
||||
// Recovery 패닉 발생 시 500 응답으로 복구하는 미들웨어
|
||||
func Recovery(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("패닉 복구: %v", err)
|
||||
http.Error(w, "내부 서버 오류가 발생했습니다.", http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// Chain 여러 미들웨어를 순서대로 체인
|
||||
func Chain(h http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
|
||||
for i := len(middlewares) - 1; i >= 0; i-- {
|
||||
h = middlewares[i](h)
|
||||
}
|
||||
return h
|
||||
}
|
||||
Reference in New Issue
Block a user