26.9:直链下载 + 过期回收 + SHA512 去重 + 防盗链 + 媒体预览 + 文件夹上传提示
CI 测试 / go vet + go test (push) Failing after 5s
CI 测试 / go vet + go test (push) Failing after 5s
- 对象存储直链:S3 引擎 302 到限时预签名 URL(有效期钳位分享剩余时效),失败自动回落代理 - 过期回收:janitor 定时扫描 + 取件惰性回收 + 管理端手动触发(POST /admin/recycle/run), retention_days 最长存储时长;删除走引用计数(去重对象安全) - SHA512 内容去重:三条上传链路落库后计算哈希,命中即复用旧对象并删除本次副本 - 下载防盗链:Referer 白名单(同源/空 Referer/通配域名放行),挂 /share/download - 取件页图片/音频内联预览(下载地址直连,加载失败回退下载按钮) - 文件夹上传:拖拽目录明确提示"建议压缩后上传"(webkitGetAsEntry 探测) - 管理端设置卡「回收与下载安全」8 个新配置键(KVSchema + configKeys + UI + i18n) - 前端产物重建并同步 server/web/dist 与 web-embed - 文档:10-config 配置表、03-file-share 直链/防盗链/文件夹章节、07-admin 回收端点、openapi
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// Package middleware — hotlink.go 下载防盗链(26.9):
|
||||
// 校验 Referer 白名单。规则:
|
||||
// - Referer 为空(直接访问/curl/浏览器地址栏):放行(不误伤正常取件);
|
||||
// - Referer 与当前请求 Host 同源:放行;
|
||||
// - Referer 主机命中管理端白名单(hotlink_whitelist,逗号分隔域名,支持 *.example.com 通配):放行;
|
||||
// - 其余一律 403。
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"fileshare/internal/config"
|
||||
)
|
||||
|
||||
// HotlinkMiddleware 返回防盗链中间件;cfg 动态读取开关与白名单(管理端改后立即生效)。
|
||||
func HotlinkMiddleware(cfg *config.Config) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if !cfg.HotlinkEnabled() {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
ref := c.GetHeader("Referer")
|
||||
if ref == "" {
|
||||
c.Next() // 空 Referer 放行
|
||||
return
|
||||
}
|
||||
u, err := url.Parse(ref)
|
||||
if err != nil || u.Host == "" {
|
||||
c.Next() // 非法 Referer 视同空,放行(避免误伤)
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(u.Host, c.Request.Host) {
|
||||
c.Next() // 同源放行
|
||||
return
|
||||
}
|
||||
if hostAllowed(u.Host, cfg.HotlinkWhitelist()) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
c.AbortWithStatusJSON(403, gin.H{"message": "防盗链:外部站点引用不允许访问该资源"})
|
||||
}
|
||||
}
|
||||
|
||||
// hostAllowed 判断主机是否命中白名单(精确匹配或 *. 通配后缀匹配)。
|
||||
// 白名单条目可带端口;通配写作 .example.com 或 *.example.com。
|
||||
func hostAllowed(host string, whitelist []string) bool {
|
||||
if len(whitelist) == 0 {
|
||||
return false
|
||||
}
|
||||
host = strings.ToLower(host)
|
||||
for _, w := range whitelist {
|
||||
w = strings.ToLower(strings.TrimSpace(w))
|
||||
w = strings.TrimPrefix(w, "*") // *.example.com → .example.com
|
||||
if w == "" {
|
||||
continue
|
||||
}
|
||||
if host == strings.TrimPrefix(w, ".") {
|
||||
return true
|
||||
}
|
||||
if strings.HasSuffix(host, w) && strings.HasPrefix(w, ".") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user