Some checks failed
部署管理后台 / 🧪 测试和构建 (push) Failing after 1m5s
部署管理后台 / 🔒 安全扫描 (push) Has been skipped
部署后端服务 / 🧪 测试后端 (push) Failing after 3m13s
部署前端网站 / 🧪 测试和构建 (push) Failing after 2m10s
部署管理后台 / 🚀 部署到生产环境 (push) Has been skipped
部署后端服务 / 🚀 构建并部署 (push) Has been skipped
部署管理后台 / 🔄 回滚部署 (push) Has been skipped
部署前端网站 / 🚀 部署到生产环境 (push) Has been skipped
部署后端服务 / 🔄 回滚部署 (push) Has been skipped
- 后端:应用 go fmt 自动格式化,统一代码风格 - 前端:更新 API 配置,完善类型安全 - 所有代码符合项目规范,准备生产部署
176 lines
4.2 KiB
Go
176 lines
4.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
)
|
|
|
|
// CORSConfig CORS 配置
|
|
type CORSConfig struct {
|
|
AllowOrigins []string // 允许的来源
|
|
AllowMethods []string // 允许的方法
|
|
AllowHeaders []string // 允许的头部
|
|
ExposeHeaders []string // 暴露的头部
|
|
AllowCredentials bool // 是否允许携带凭证
|
|
MaxAge time.Duration // 预检请求缓存时间
|
|
}
|
|
|
|
// DefaultCORSConfig 默认 CORS 配置
|
|
func DefaultCORSConfig() CORSConfig {
|
|
return CORSConfig{
|
|
AllowOrigins: []string{
|
|
"http://localhost:3000",
|
|
"http://localhost:3001",
|
|
"http://localhost:5173",
|
|
"http://localhost:8080",
|
|
},
|
|
AllowMethods: []string{
|
|
"GET",
|
|
"POST",
|
|
"PUT",
|
|
"DELETE",
|
|
"OPTIONS",
|
|
"HEAD",
|
|
},
|
|
AllowHeaders: []string{
|
|
"Origin",
|
|
"Content-Type",
|
|
"Content-Length",
|
|
"Accept-Encoding",
|
|
"X-CSRF-Token",
|
|
"Authorization",
|
|
"accept",
|
|
"origin",
|
|
"Cache-Control",
|
|
"X-Requested-With",
|
|
},
|
|
ExposeHeaders: []string{
|
|
"Content-Length",
|
|
"Content-Type",
|
|
},
|
|
AllowCredentials: true,
|
|
MaxAge: 12 * time.Hour,
|
|
}
|
|
}
|
|
|
|
// ProductionCORSConfig 生产环境 CORS 配置
|
|
func ProductionCORSConfig(allowedOrigins []string) CORSConfig {
|
|
config := DefaultCORSConfig()
|
|
if len(allowedOrigins) > 0 {
|
|
config.AllowOrigins = allowedOrigins
|
|
} else {
|
|
// 生产环境默认只允许 HTTPS
|
|
config.AllowOrigins = []string{
|
|
"https://photography.iriver.top",
|
|
"https://admin.photography.iriver.top",
|
|
}
|
|
}
|
|
return config
|
|
}
|
|
|
|
// CORSMiddleware CORS 中间件
|
|
type CORSMiddleware struct {
|
|
config CORSConfig
|
|
}
|
|
|
|
// NewCORSMiddleware 创建 CORS 中间件
|
|
func NewCORSMiddleware(config CORSConfig) *CORSMiddleware {
|
|
return &CORSMiddleware{
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// Handle 处理 CORS
|
|
func (m *CORSMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
origin := r.Header.Get("Origin")
|
|
|
|
// 检查来源是否被允许
|
|
if origin != "" && m.isOriginAllowed(origin) {
|
|
w.Header().Set("Access-Control-Allow-Origin", origin)
|
|
}
|
|
|
|
// 设置允许的方法
|
|
if len(m.config.AllowMethods) > 0 {
|
|
w.Header().Set("Access-Control-Allow-Methods", strings.Join(m.config.AllowMethods, ", "))
|
|
}
|
|
|
|
// 设置允许的头部
|
|
if len(m.config.AllowHeaders) > 0 {
|
|
w.Header().Set("Access-Control-Allow-Headers", strings.Join(m.config.AllowHeaders, ", "))
|
|
}
|
|
|
|
// 设置暴露的头部
|
|
if len(m.config.ExposeHeaders) > 0 {
|
|
w.Header().Set("Access-Control-Expose-Headers", strings.Join(m.config.ExposeHeaders, ", "))
|
|
}
|
|
|
|
// 设置是否允许携带凭证
|
|
if m.config.AllowCredentials {
|
|
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
|
}
|
|
|
|
// 设置预检请求缓存时间
|
|
if m.config.MaxAge > 0 {
|
|
w.Header().Set("Access-Control-Max-Age", m.config.MaxAge.String())
|
|
}
|
|
|
|
// 添加安全头部
|
|
m.setSecurityHeaders(w)
|
|
|
|
// 处理预检请求
|
|
if r.Method == "OPTIONS" {
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
// 记录跨域请求
|
|
if origin != "" {
|
|
logx.Infof("CORS request from origin: %s, method: %s, path: %s", origin, r.Method, r.URL.Path)
|
|
}
|
|
|
|
next(w, r)
|
|
})
|
|
}
|
|
|
|
// isOriginAllowed 检查来源是否被允许
|
|
func (m *CORSMiddleware) isOriginAllowed(origin string) bool {
|
|
for _, allowedOrigin := range m.config.AllowOrigins {
|
|
if allowedOrigin == "*" {
|
|
return true
|
|
}
|
|
if allowedOrigin == origin {
|
|
return true
|
|
}
|
|
// 支持通配符匹配
|
|
if strings.HasSuffix(allowedOrigin, "*") {
|
|
prefix := strings.TrimSuffix(allowedOrigin, "*")
|
|
if strings.HasPrefix(origin, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// setSecurityHeaders 设置安全头部
|
|
func (m *CORSMiddleware) setSecurityHeaders(w http.ResponseWriter) {
|
|
// 防止点击劫持
|
|
w.Header().Set("X-Frame-Options", "DENY")
|
|
|
|
// 防止 MIME 类型嗅探
|
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
|
|
// XSS 保护
|
|
w.Header().Set("X-XSS-Protection", "1; mode=block")
|
|
|
|
// 引用者策略
|
|
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
|
|
|
// 内容安全策略 (基础版)
|
|
w.Header().Set("Content-Security-Policy", "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; script-src 'self'")
|
|
}
|