feat: update openapi page logic

This commit is contained in:
engigu
2026-03-10 16:21:09 +08:00
parent 27a7bfb147
commit 03ee39fccf
3 changed files with 42 additions and 21 deletions
+28 -12
View File
@@ -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() _, 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() c.Next()
return return
} }
// 未提供认证,触发浏览器登录弹窗 // 验证失败,不再返回 WWW-Authenticate 头触发浏览器反人类原生弹窗
if !hasAuth { // 我们返回 401 的 JSON 或纯文本结构,以便由调用方自行接管鉴权逻辑
c.Header("WWW-Authenticate", `Basic realm="OpenAPI Access Token (Any username)"`) c.JSON(http.StatusUnauthorized, gin.H{
c.Status(http.StatusUnauthorized) "code": 401,
c.Abort() "msg": "OpenAPI 访问未授权或 Token 错误",
return })
}
// 认证失败 (密码错误),返回 404 隐藏路由
c.Status(http.StatusNotFound)
c.Abort() c.Abort()
} }
} }
+2 -6
View File
@@ -40,13 +40,9 @@ func initOpenAPIRoutes(root *gin.RouterGroup, urlPrefix string) {
return return
} }
// 执行认证 // 执行主站 Cookie 认证
middleware.SwaggerAuth()(c) middleware.AuthRequired()(c)
if c.IsAborted() { if c.IsAborted() {
// 如果认证失败(且被中间件置为 404,如密码错误且我们想要隐藏它)
if c.Writer.Status() == http.StatusNotFound {
serveSPA(c, urlPrefix, 404)
}
return return
} }
+12 -3
View File
@@ -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) onMounted(loadSettings)
</script> </script>
@@ -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">推荐方式</Badge> class="font-normal text-xs bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20 whitespace-nowrap">推荐方式</Badge>
</div> </div>
<div class="flex items-center justify-between sm:justify-end w-full sm:w-auto gap-4"> <div class="flex items-center justify-between sm:justify-end w-full sm:w-auto gap-4">
<a :href="baseUrl + '/openapi/index.html'" target="_blank" <a href="#" @click.prevent="openSwaggerDocs"
class="flex items-center gap-1 text-xs text-blue-600 hover:underline shrink-0"> class="flex items-center gap-1 text-xs text-blue-600 hover:underline shrink-0">
查看接口文档 查看接口文档
<ExternalLink class="w-3 h-3" /> <ExternalLink class="w-3 h-3" />
</a> </a>
<div class="flex items-center gap-2 shrink-0"> <div class="flex items-center gap-2 shrink-0">
<Switch v-model="form.openapi_enabled" id="openapi-enabled" /> <Switch v-model="form.openapi_enabled" id="openapi-enabled" />
<Label for="openapi-enabled" class="text-xs cursor-pointer">开启鉴权</Label> <Label for="openapi-enabled" class="text-xs cursor-pointer">开启 OpenAPI</Label>
</div> </div>
</div> </div>
</div> </div>
<p class="text-sm text-muted-foreground mb-4">开启全局 OpenAPI 直接访问能力配置后可通过请求头 <code <p class="text-sm text-muted-foreground mb-4">开启全局 OpenAPI 直接访问能力配置后可通过请求头 <code
class="bg-muted px-1.5 py-0.5 rounded text-xs select-all font-sans">Authorization: Bearer &lt;在此生成的Token&gt;</code> class="bg-muted px-1.5 py-0.5 rounded text-xs select-all font-sans">Authorization: Bearer &lt;在此生成的Token&gt;</code>
无需登录直接调用系统的所有接口请妥善保管并设置合理的有效期<span class="text-amber-600 dark:text-amber-500 font-medium ml-1">注意必须先开启鉴权开关才能查看接口文档页面和进行调用</span></p> 以第三方身份调用系统的所有接口请妥善保管 Token 并设置合理的有效期<span class="text-amber-600 dark:text-amber-500 font-medium ml-1">注意必须先开启本功能才能查看接口文档页面和对接调用</span></p>
<div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4 mb-4"> <div class="grid grid-cols-1 sm:grid-cols-4 items-center gap-2 sm:gap-4 mb-4">
<Label class="sm:text-right text-muted-foreground">Token 密钥</Label> <Label class="sm:text-right text-muted-foreground">Token 密钥</Label>
@@ -303,5 +311,6 @@ onMounted(loadSettings)
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
</div> </div>
</template> </template>