diff --git a/app/http/controllers/swagger_controller.go b/app/http/controllers/swagger_controller.go index 1606c544..8294fd52 100644 --- a/app/http/controllers/swagger_controller.go +++ b/app/http/controllers/swagger_controller.go @@ -2,7 +2,6 @@ package controllers import ( "github.com/goravel/framework/contracts/http" - "github.com/goravel/framework/facades" "github.com/swaggo/http-swagger/v2" _ "github.com/TheTNB/panel/docs" @@ -25,10 +24,6 @@ func NewSwaggerController() *SwaggerController { // @Failure 500 // @Router /swagger [get] func (r *SwaggerController) Index(ctx http.Context) http.Response { - if !facades.Config().GetBool("app.debug") { - return Error(ctx, http.StatusNotFound, http.StatusText(http.StatusNotFound)) - } - handler := httpSwagger.Handler() handler(ctx.Response().Writer(), ctx.Request().Origin()) diff --git a/app/http/controllers/user_controller.go b/app/http/controllers/user_controller.go index 9def3ab6..f9992b22 100644 --- a/app/http/controllers/user_controller.go +++ b/app/http/controllers/user_controller.go @@ -1,8 +1,11 @@ package controllers import ( + "fmt" + "github.com/goravel/framework/contracts/http" "github.com/goravel/framework/facades" + "github.com/spf13/cast" "github.com/TheTNB/panel/app/http/requests/user" "github.com/TheTNB/panel/app/models" @@ -20,16 +23,15 @@ func NewUserController() *UserController { // Login // -// @Summary 登录 -// @Description 通过用户名和密码获取访问令牌 -// @Tags 用户鉴权 -// @Accept json -// @Produce json -// @Param data body requests.Login true "request" -// @Success 200 {object} SuccessResponse -// @Failure 403 {object} ErrorResponse "用户名或密码错误" -// @Failure 500 {object} ErrorResponse "系统内部错误 -// @Router /panel/user/login [post] +// @Summary 登录 +// @Tags 用户鉴权 +// @Accept json +// @Produce json +// @Param data body requests.Login true "request" +// @Success 200 {object} SuccessResponse +// @Failure 403 {object} ErrorResponse "用户名或密码错误" +// @Failure 500 {object} ErrorResponse "系统内部错误 +// @Router /panel/user/login [post] func (r *UserController) Login(ctx http.Context) http.Response { var loginRequest requests.Login sanitize := SanitizeRequest(ctx, &loginRequest) @@ -60,32 +62,38 @@ func (r *UserController) Login(ctx http.Context) http.Response { } } - token, loginErr := facades.Auth(ctx).LoginUsingID(user.ID) - if loginErr != nil { - facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{ - "error": err.Error(), - }).Info("登录失败") - return ErrorSystem(ctx) + ctx.Request().Session().Put("user_id", user.ID) + return Success(ctx, nil) +} + +// Logout +// +// @Summary 登出 +// @Tags 用户鉴权 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse +// @Router /panel/user/logout [post] +func (r *UserController) Logout(ctx http.Context) http.Response { + if err := ctx.Request().Session().Invalidate(); err != nil { + return Error(ctx, http.StatusInternalServerError, fmt.Sprintf("登出失败: %s", err.Error())) } - return Success(ctx, http.Json{ - "access_token": token, - }) + return Success(ctx, nil) } // Info // -// @Summary 用户信息 -// @Description 获取当前登录用户信息 -// @Tags 用户鉴权 -// @Produce json -// @Security BearerToken -// @Success 200 {object} SuccessResponse -// @Router /panel/user/info [get] +// @Summary 用户信息 +// @Tags 用户鉴权 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse +// @Router /panel/user/info [get] func (r *UserController) Info(ctx http.Context) http.Response { + userID := cast.ToUint(ctx.Value("user_id")) var user models.User - err := facades.Auth(ctx).User(&user) - if err != nil { + if err := facades.Orm().Query().Where("id", userID).Get(&user); err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "用户").With(map[string]any{ "error": err.Error(), }).Info("获取用户信息失败") diff --git a/app/http/middleware/jwt.go b/app/http/middleware/jwt.go deleted file mode 100644 index 1e6bc8f8..00000000 --- a/app/http/middleware/jwt.go +++ /dev/null @@ -1,47 +0,0 @@ -package middleware - -import ( - "errors" - - "github.com/goravel/framework/auth" - "github.com/goravel/framework/contracts/http" - "github.com/goravel/framework/facades" -) - -// Jwt 确保通过 JWT 鉴权 -func Jwt() http.Middleware { - return func(ctx http.Context) { - translate := facades.Lang(ctx) - token := ctx.Request().Header("Authorization", ctx.Request().Header("Sec-WebSocket-Protocol")) - if len(token) == 0 { - ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ - "message": translate.Get("auth.token.missing"), - }) - return - } - - // JWT 鉴权 - if _, err := facades.Auth(ctx).Parse(token); err != nil { - if errors.Is(err, auth.ErrorTokenExpired) { - token, err = facades.Auth(ctx).Refresh() - if err != nil { - // 到达刷新时间上限 - ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ - "message": translate.Get("auth.token.expired"), - }) - return - } - - token = "Bearer " + token - } else { - ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ - "message": translate.Get("auth.token.expired"), - }) - return - } - } - - ctx.Response().Header("Authorization", token) - ctx.Request().Next() - } -} diff --git a/app/http/middleware/session.go b/app/http/middleware/session.go new file mode 100644 index 00000000..8785b03d --- /dev/null +++ b/app/http/middleware/session.go @@ -0,0 +1,39 @@ +package middleware + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + "github.com/spf13/cast" +) + +// Session 确保通过 JWT 鉴权 +func Session() http.Middleware { + return func(ctx http.Context) { + translate := facades.Lang(ctx) + + if !ctx.Request().HasSession() { + ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ + "message": translate.Get("auth.session.missing"), + }) + return + } + + if ctx.Request().Session().Missing("user_id") { + ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ + "message": translate.Get("auth.session.expired"), + }) + return + } + + userID := cast.ToUint(ctx.Request().Session().Get("user_id")) + if userID == 0 { + ctx.Request().AbortWithStatusJson(http.StatusUnauthorized, http.Json{ + "message": translate.Get("auth.session.invalid"), + }) + return + } + + ctx.WithValue("user_id", userID) + ctx.Request().Next() + } +} diff --git a/docs/docs.go b/docs/docs.go index 979a5107..ed3cbbe1 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -3235,7 +3235,6 @@ const docTemplate = `{ "BearerToken": [] } ], - "description": "获取当前登录用户信息", "produces": [ "application/json" ], @@ -3255,7 +3254,6 @@ const docTemplate = `{ }, "/panel/user/login": { "post": { - "description": "通过用户名和密码获取访问令牌", "consumes": [ "application/json" ], @@ -3299,6 +3297,30 @@ const docTemplate = `{ } } }, + "/panel/user/logout": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户鉴权" + ], + "summary": "登出", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, "/panel/website/backupList": { "get": { "security": [ diff --git a/docs/swagger.json b/docs/swagger.json index e438c380..bc9f45d9 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -3228,7 +3228,6 @@ "BearerToken": [] } ], - "description": "获取当前登录用户信息", "produces": [ "application/json" ], @@ -3248,7 +3247,6 @@ }, "/panel/user/login": { "post": { - "description": "通过用户名和密码获取访问令牌", "consumes": [ "application/json" ], @@ -3292,6 +3290,30 @@ } } }, + "/panel/user/logout": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户鉴权" + ], + "summary": "登出", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + } + } + } + }, "/panel/website/backupList": { "get": { "security": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 9ddc1e92..4cd2f3e9 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -2645,7 +2645,6 @@ paths: - 系统 /panel/user/info: get: - description: 获取当前登录用户信息 produces: - application/json responses: @@ -2662,7 +2661,6 @@ paths: post: consumes: - application/json - description: 通过用户名和密码获取访问令牌 parameters: - description: request in: body @@ -2688,6 +2686,20 @@ paths: summary: 登录 tags: - 用户鉴权 + /panel/user/logout: + post: + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + security: + - BearerToken: [] + summary: 登出 + tags: + - 用户鉴权 /panel/website/backupList: get: parameters: diff --git a/lang/en.json b/lang/en.json index 434a4650..af8db26f 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,8 +1,9 @@ { "auth": { - "token": { - "expired": "login has expired", - "missing": "not logged in" + "session": { + "expired": "session has expired", + "missing": "Please enable cookies and try again", + "invalid": "invalid session" } }, "commands": { diff --git a/lang/zh_CN.json b/lang/zh_CN.json index 793d65aa..3cbffd00 100644 --- a/lang/zh_CN.json +++ b/lang/zh_CN.json @@ -1,8 +1,9 @@ { "auth": { - "token": { - "expired": "登录已过期", - "missing": "未登录" + "session": { + "expired": "会话已过期", + "missing": "请启用 Cookie 后再试", + "invalid": "会话无效" } }, "commands": { diff --git a/routes/api.go b/routes/api.go index 8376f912..a188f21c 100644 --- a/routes/api.go +++ b/routes/api.go @@ -14,29 +14,30 @@ func Api() { r.Prefix("info").Group(func(r route.Router) { infoController := controllers.NewInfoController() r.Get("panel", infoController.Panel) - r.Middleware(middleware.Jwt()).Get("homePlugins", infoController.HomePlugins) - r.Middleware(middleware.Jwt()).Get("nowMonitor", infoController.NowMonitor) - r.Middleware(middleware.Jwt()).Get("systemInfo", infoController.SystemInfo) - r.Middleware(middleware.Jwt()).Get("countInfo", infoController.CountInfo) - r.Middleware(middleware.Jwt()).Get("installedDbAndPhp", infoController.InstalledDbAndPhp) - r.Middleware(middleware.Jwt()).Get("checkUpdate", infoController.CheckUpdate) - r.Middleware(middleware.Jwt()).Get("updateInfo", infoController.UpdateInfo) - r.Middleware(middleware.Jwt()).Post("update", infoController.Update) - r.Middleware(middleware.Jwt()).Post("restart", infoController.Restart) + r.Middleware(middleware.Session()).Get("homePlugins", infoController.HomePlugins) + r.Middleware(middleware.Session()).Get("nowMonitor", infoController.NowMonitor) + r.Middleware(middleware.Session()).Get("systemInfo", infoController.SystemInfo) + r.Middleware(middleware.Session()).Get("countInfo", infoController.CountInfo) + r.Middleware(middleware.Session()).Get("installedDbAndPhp", infoController.InstalledDbAndPhp) + r.Middleware(middleware.Session()).Get("checkUpdate", infoController.CheckUpdate) + r.Middleware(middleware.Session()).Get("updateInfo", infoController.UpdateInfo) + r.Middleware(middleware.Session()).Post("update", infoController.Update) + r.Middleware(middleware.Session()).Post("restart", infoController.Restart) }) r.Prefix("user").Group(func(r route.Router) { userController := controllers.NewUserController() r.Middleware(frameworkmiddleware.Throttle("login")).Post("login", userController.Login) - r.Middleware(middleware.Jwt()).Get("info", userController.Info) + r.Post("logout", userController.Logout) + r.Middleware(middleware.Session()).Get("info", userController.Info) }) - r.Prefix("task").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("task").Middleware(middleware.Session()).Group(func(r route.Router) { taskController := controllers.NewTaskController() r.Get("status", taskController.Status) r.Get("list", taskController.List) r.Get("log", taskController.Log) r.Post("delete", taskController.Delete) }) - r.Prefix("website").Middleware(middleware.Jwt(), middleware.MustInstall()).Group(func(r route.Router) { + r.Prefix("website").Middleware(middleware.Session(), middleware.MustInstall()).Group(func(r route.Router) { websiteController := controllers.NewWebsiteController() r.Get("defaultConfig", websiteController.GetDefaultConfig) r.Post("defaultConfig", websiteController.SaveDefaultConfig) @@ -44,7 +45,7 @@ func Api() { r.Put("uploadBackup", websiteController.UploadBackup) r.Delete("deleteBackup", websiteController.DeleteBackup) }) - r.Prefix("websites").Middleware(middleware.Jwt(), middleware.MustInstall()).Group(func(r route.Router) { + r.Prefix("websites").Middleware(middleware.Session(), middleware.MustInstall()).Group(func(r route.Router) { websiteController := controllers.NewWebsiteController() r.Get("/", websiteController.List) r.Post("/", websiteController.Add) @@ -58,7 +59,7 @@ func Api() { r.Post("{id}/resetConfig", websiteController.ResetConfig) r.Post("{id}/status", websiteController.Status) }) - r.Prefix("cert").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("cert").Middleware(middleware.Session()).Group(func(r route.Router) { certController := controllers.NewCertController() r.Get("caProviders", certController.CAProviders) r.Get("dnsProviders", certController.DNSProviders) @@ -83,7 +84,7 @@ func Api() { r.Post("manualDNS", certController.ManualDNS) r.Post("deploy", certController.Deploy) }) - r.Prefix("plugin").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("plugin").Middleware(middleware.Session()).Group(func(r route.Router) { pluginController := controllers.NewPluginController() r.Get("list", pluginController.List) r.Post("install", pluginController.Install) @@ -92,7 +93,7 @@ func Api() { r.Post("updateShow", pluginController.UpdateShow) r.Get("isInstalled", pluginController.IsInstalled) }) - r.Prefix("cron").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("cron").Middleware(middleware.Session()).Group(func(r route.Router) { cronController := controllers.NewCronController() r.Get("list", cronController.List) r.Get("{id}", cronController.Script) @@ -102,7 +103,7 @@ func Api() { r.Post("status", cronController.Status) r.Get("log/{id}", cronController.Log) }) - r.Prefix("safe").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("safe").Middleware(middleware.Session()).Group(func(r route.Router) { safeController := controllers.NewSafeController() r.Get("firewallStatus", safeController.GetFirewallStatus) r.Post("firewallStatus", safeController.SetFirewallStatus) @@ -116,7 +117,7 @@ func Api() { r.Get("pingStatus", safeController.GetPingStatus) r.Post("pingStatus", safeController.SetPingStatus) }) - r.Prefix("container").Middleware(middleware.Jwt(), middleware.MustInstall()).Group(func(r route.Router) { + r.Prefix("container").Middleware(middleware.Session(), middleware.MustInstall()).Group(func(r route.Router) { containerController := controllers.NewContainerController() r.Get("list", containerController.ContainerList) r.Get("search", containerController.ContainerSearch) @@ -164,7 +165,7 @@ func Api() { r.Post("prune", containerController.VolumePrune) }) }) - r.Prefix("file").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("file").Middleware(middleware.Session()).Group(func(r route.Router) { fileController := controllers.NewFileController() r.Post("create", fileController.Create) r.Get("content", fileController.Content) @@ -182,7 +183,7 @@ func Api() { r.Post("search", fileController.Search) r.Get("list", fileController.List) }) - r.Prefix("monitor").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("monitor").Middleware(middleware.Session()).Group(func(r route.Router) { monitorController := controllers.NewMonitorController() r.Post("switch", monitorController.Switch) r.Post("saveDays", monitorController.SaveDays) @@ -190,20 +191,20 @@ func Api() { r.Get("list", monitorController.List) r.Get("switchAndDays", monitorController.SwitchAndDays) }) - r.Prefix("ssh").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("ssh").Middleware(middleware.Session()).Group(func(r route.Router) { sshController := controllers.NewSshController() r.Get("info", sshController.GetInfo) r.Post("info", sshController.UpdateInfo) r.Get("session", sshController.Session) }) - r.Prefix("setting").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("setting").Middleware(middleware.Session()).Group(func(r route.Router) { settingController := controllers.NewSettingController() r.Get("list", settingController.List) r.Post("update", settingController.Update) r.Get("https", settingController.GetHttps) r.Post("https", settingController.UpdateHttps) }) - r.Prefix("system").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("system").Middleware(middleware.Session()).Group(func(r route.Router) { controller := controllers.NewSystemController() r.Get("service/status", controller.ServiceStatus) r.Get("service/isEnabled", controller.ServiceIsEnabled) @@ -218,7 +219,7 @@ func Api() { // 文档 swaggerController := controllers.NewSwaggerController() - facades.Route().Get("swagger/*any", swaggerController.Index) + facades.Route().Middleware(middleware.Session()).Get("swagger/*any", swaggerController.Index) // 静态文件 entrance := facades.Config().GetString("http.entrance") diff --git a/routes/plugin.go b/routes/plugin.go index 7eab2d52..de3ff9c4 100644 --- a/routes/plugin.go +++ b/routes/plugin.go @@ -10,7 +10,7 @@ import ( // Plugin 加载插件路由 func Plugin() { - facades.Route().Prefix("api/plugins").Middleware(middleware.Jwt(), middleware.MustInstall()).Group(func(r route.Router) { + facades.Route().Prefix("api/plugins").Middleware(middleware.Session(), middleware.MustInstall()).Group(func(r route.Router) { r.Prefix("openresty").Group(func(route route.Router) { openRestyController := plugins.NewOpenrestyController() route.Get("load", openRestyController.Load)