v1.3.5: add CORS whitelist support (f.CORS) covering /cell and all custom routes

This commit is contained in:
2026-09-09 11:31:22 +08:00
parent cfecbff8f8
commit a4993502bc
6 changed files with 236 additions and 6 deletions
+71
View File
@@ -0,0 +1,71 @@
package fun
import (
"strings"
"github.com/valyala/fasthttp"
)
const (
corsMaxAge = "86400" // 预检结果缓存 24h,减少浏览器 OPTIONS 探测
)
// CORS 配置跨域来源白名单,须在 Start 前调用;作用于全部端点(/cell、自定义路由与通配路由)。
// 按需放行:仅列出的来源会被放行——请求 Origin 命中白名单时回显该来源并允许
// 携带凭据(Cookie),未命中则不附加 CORS 头(浏览器侧自然拦截)。
//
// f.CORS("https://a.com", "https://b.com")
//
// 预检请求(OPTIONS 且带 Access-Control-Request-Method 头)直接以 204 应答,
// 不进入路由与 /cell 处理。
func (f *Fun) CORS(origins ...string) {
f.mustNotStarted("CORS")
whitelist := make(map[string]struct{}, len(origins))
for _, o := range origins {
if o != "" {
whitelist[strings.ToLower(o)] = struct{}{}
}
}
f.corsOrigins = whitelist
}
// corsEnabled CORS 是否已配置
func (f *Fun) corsEnabled() bool {
return len(f.corsOrigins) > 0
}
// handleCors 处理 CORS:Origin 命中白名单的请求附加跨域响应头,
// 预检请求(OPTIONS + Access-Control-Request-Method)在此直接应答并返回 true。
func (f *Fun) handleCors(fastCtx *fasthttp.RequestCtx) bool {
if !f.corsEnabled() {
return false
}
origin := string(fastCtx.Request.Header.Peek("Origin"))
if origin == "" {
return false // 同源请求与服务器到服务器调用,无跨域语义
}
if _, ok := f.corsOrigins[strings.ToLower(origin)]; !ok {
return false // 未命中白名单:不加 CORS 头,交给浏览器拦截
}
h := &fastCtx.Response.Header
h.Set("Access-Control-Allow-Origin", origin)
h.Set("Access-Control-Allow-Credentials", "true")
// 放行结果随 Origin 变化,防中间层缓存错配
h.Add("Vary", "Origin")
if !fastCtx.IsOptions() || fastCtx.Request.Header.Peek("Access-Control-Request-Method") == nil {
return false // 实际请求:头已附加,继续正常路由
}
// 预检请求:回显浏览器声明的目标方法与请求头,24h 内同请求免预检
h.Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
if reqHeaders := fastCtx.Request.Header.Peek("Access-Control-Request-Headers"); len(reqHeaders) > 0 {
h.Set("Access-Control-Allow-Headers", string(reqHeaders))
} else {
h.Set("Access-Control-Allow-Headers", "Content-Type")
}
h.Set("Access-Control-Max-Age", corsMaxAge)
fastCtx.SetStatusCode(fasthttp.StatusNoContent)
return true
}