From 03ee39fccf3ddadbd04ec447a723fc3df96a8928 Mon Sep 17 00:00:00 2001
From: engigu
Date: Tue, 10 Mar 2026 16:21:09 +0800
Subject: [PATCH] feat: update openapi page logic
---
internal/middleware/auth.go | 40 +++++++++++++++++--------
internal/router/openapi_routes.go | 8 ++---
web/src/views/settings/SiteSettings.vue | 15 ++++++++--
3 files changed, 42 insertions(+), 21 deletions(-)
diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go
index 54b83ce..e424264 100644
--- a/internal/middleware/auth.go
+++ b/internal/middleware/auth.go
@@ -182,23 +182,39 @@ func SwaggerAuth() gin.HandlerFunc {
}
}
+ // 获取请求中携带的凭证
+ // 1. URL 参数 token
+ // 2. Cookie 中的 openapi_token
+ // 3. HTTP Basic Auth
+ tokenQuery := c.Query("token")
+ tokenCookie, _ := c.Cookie("openapi_token")
_, password, hasAuth := c.Request.BasicAuth()
- // 允许使用任意用户名,但密码必须匹配 OpenAPI Token
- if hasAuth && password == tokenConfig.Token && tokenConfig.Token != "" {
+
+ var providedToken string
+ if tokenQuery != "" {
+ providedToken = tokenQuery
+ } else if tokenCookie != "" {
+ providedToken = tokenCookie
+ } else if hasAuth {
+ providedToken = password
+ }
+
+ // 检查提供的 token 是否匹配
+ if providedToken != "" && providedToken == tokenConfig.Token {
+ // 如果是通过 url 参数进来的,自动将其种入 Cookie,便于后续加载静态资源 (如 json)
+ if tokenQuery != "" {
+ c.SetCookie("openapi_token", providedToken, 86400, "/openapi", "", false, false)
+ }
c.Next()
return
}
- // 未提供认证,触发浏览器登录弹窗
- if !hasAuth {
- c.Header("WWW-Authenticate", `Basic realm="OpenAPI Access Token (Any username)"`)
- c.Status(http.StatusUnauthorized)
- c.Abort()
- return
- }
-
- // 认证失败 (密码错误),返回 404 隐藏路由
- c.Status(http.StatusNotFound)
+ // 验证失败,不再返回 WWW-Authenticate 头触发浏览器反人类原生弹窗
+ // 我们返回 401 的 JSON 或纯文本结构,以便由调用方自行接管鉴权逻辑
+ c.JSON(http.StatusUnauthorized, gin.H{
+ "code": 401,
+ "msg": "OpenAPI 访问未授权或 Token 错误",
+ })
c.Abort()
}
}
diff --git a/internal/router/openapi_routes.go b/internal/router/openapi_routes.go
index 16020b4..bb8256c 100644
--- a/internal/router/openapi_routes.go
+++ b/internal/router/openapi_routes.go
@@ -40,13 +40,9 @@ func initOpenAPIRoutes(root *gin.RouterGroup, urlPrefix string) {
return
}
- // 执行认证
- middleware.SwaggerAuth()(c)
+ // 执行主站 Cookie 认证
+ middleware.AuthRequired()(c)
if c.IsAborted() {
- // 如果认证失败(且被中间件置为 404,如密码错误且我们想要隐藏它)
- if c.Writer.Status() == http.StatusNotFound {
- serveSPA(c, urlPrefix, 404)
- }
return
}
diff --git a/web/src/views/settings/SiteSettings.vue b/web/src/views/settings/SiteSettings.vue
index 1a7ab16..14f8231 100644
--- a/web/src/views/settings/SiteSettings.vue
+++ b/web/src/views/settings/SiteSettings.vue
@@ -112,6 +112,14 @@ async function copyOpenapiToken() {
}
}
+function openSwaggerDocs() {
+ if (!form.value.openapi_enabled) {
+ toast.error('必须先开启 OpenAPI 才能查看接口文档页面')
+ return
+ }
+ window.open(`${baseUrl}/openapi/index.html`, '_blank')
+}
+
onMounted(loadSettings)
@@ -240,20 +248,20 @@ onMounted(loadSettings)
class="font-normal text-xs bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20 whitespace-nowrap">推荐方式
开启全局 OpenAPI 直接访问能力,配置后可通过请求头 Authorization: Bearer <在此生成的Token>
- 无需登录直接调用系统的所有接口,请妥善保管并设置合理的有效期。注意:必须先开启鉴权开关才能查看接口文档页面和进行调用。
+ 以第三方身份调用系统的所有接口,请妥善保管 Token 并设置合理的有效期。注意:必须先开启本功能才能查看接口文档页面和对接调用。
@@ -303,5 +311,6 @@ onMounted(loadSettings)
+