2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 13:47:15 +08:00
Files
panel/internal/http/middleware/entrance.go

123 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package middleware
import (
"github.com/go-chi/chi/v5"
"net"
"net/http"
"slices"
"strings"
"github.com/go-rat/chix"
"github.com/go-rat/sessions"
"github.com/knadh/koanf/v2"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"github.com/tnb-labs/panel/pkg/punycode"
)
// Entrance 确保通过正确的入口访问
func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
sess, err := session.GetSession(r)
if err != nil {
render := chix.NewRender(w)
defer render.Release()
render.Status(http.StatusInternalServerError)
render.JSON(chix.M{
"msg": err.Error(),
})
return
}
entrance := strings.TrimSuffix(conf.String("http.entrance"), "/")
if entrance == "" {
entrance = "/"
}
if !strings.HasPrefix(entrance, "/") {
entrance = "/" + entrance
}
// 情况一设置了绑定域名、IP、UA且请求不符合要求返回错误
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
host = r.Host
}
if strings.Contains(host, "xn--") {
if decoded, err := punycode.DecodeDomain(host); err == nil {
host = decoded
}
}
if len(conf.Strings("http.bind_domain")) > 0 && !slices.Contains(conf.Strings("http.bind_domain"), host) {
render := chix.NewRender(w)
defer render.Release()
render.Status(http.StatusTeapot)
render.JSON(chix.M{
"msg": t.Get("invalid request domain: %s", r.Host),
})
return
}
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
ip = r.RemoteAddr
}
if len(conf.Strings("http.bind_ip")) > 0 && !slices.Contains(conf.Strings("http.bind_ip"), ip) {
render := chix.NewRender(w)
defer render.Release()
render.Status(http.StatusTeapot)
render.JSON(chix.M{
"msg": t.Get("invalid request ip: %s", ip),
})
return
}
if len(conf.Strings("http.bind_ua")) > 0 && !slices.Contains(conf.Strings("http.bind_ua"), r.UserAgent()) {
render := chix.NewRender(w)
defer render.Release()
render.Status(http.StatusTeapot)
render.JSON(chix.M{
"msg": t.Get("invalid request user agent: %s", r.UserAgent()),
})
return
}
// 情况二:请求路径与入口路径相同,标记通过验证并重定向到登录页面
if strings.TrimSuffix(r.URL.Path, "/") == entrance {
sess.Put("verify_entrance", true)
render := chix.NewRender(w, r)
defer render.Release()
render.Redirect("/login")
return
}
// 情况三通过APIKey+入口路径访问,重写请求路径并跳过验证
if strings.HasPrefix(r.URL.Path, entrance) && r.Header.Get("Authorization") != "" {
// 只在设置了入口路径的情况下,才进行重写
if entrance != "/" {
if rctx := chi.RouteContext(r.Context()); rctx != nil {
rctx.RoutePath = strings.TrimPrefix(rctx.RoutePath, entrance)
}
r.URL.Path = strings.TrimPrefix(r.URL.Path, entrance)
}
next.ServeHTTP(w, r)
return
}
// 情况三:非调试模式且未通过验证的请求,返回错误
if !conf.Bool("app.debug") &&
!cast.ToBool(sess.Get("verify_entrance", false)) &&
r.URL.Path != "/robots.txt" {
render := chix.NewRender(w)
defer render.Release()
render.Status(http.StatusTeapot)
render.JSON(chix.M{
"msg": t.Get("invalid access entrance"),
})
return
}
next.ServeHTTP(w, r)
})
}
}