fix: 按评审报告止血支付漏洞并加固授权链路

废除浏览器回跳自助结算,通用 webhook 强制密钥鉴权,Waffo 支持验签与未知事件忽略;重构订单状态机/激活占座/SQLite WAL,并补备份、readyz、限流与前端导出/登录页安全修复。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-12 21:26:58 +08:00
1 parent 8cc2c83f9d
commit bac72e6945
24 files changed
+682 -391

No files matched your search

+31 -15
View File
@@ -66,10 +66,22 @@ func SetupRouter(db *gorm.DB, cfg *config.Config) *gin.Engine {
// CORS 中间件 // Logan.Li
r.Use(middleware.CORSMiddleware(cfg))
// 健康检查 // Logan.Li
// 存活探针(不查库) // Logan.Li
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"code": 0, "message": "ok", "data": nil})
})
r.GET("/healthz", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "alive"})
})
// 就绪探针:检查数据库可达 // Logan.Li
r.GET("/readyz", func(c *gin.Context) {
sqlDB, err := db.DB()
if err != nil || sqlDB.Ping() != nil {
c.JSON(503, gin.H{"status": "db unreachable"})
return
}
c.JSON(200, gin.H{"status": "ready"})
})
// 初始化控制器 // Logan.Li
authController := auth.NewAuthController(db, cfg)
@@ -87,36 +99,40 @@ func SetupRouter(db *gorm.DB, cfg *config.Config) *gin.Engine {
// ============ 认证路由(无需 JWT) ============
authGroup := api.Group("/auth")
{
register(authGroup, "POST", "/login", authController.Login)
register(authGroup, "POST", "/login", middleware.RateLimit(0.2, 5), authController.Login)
}
// ============ 客户端授权契约(无需认证,供外部工具调用) ============
clientAPI := api.Group("/v1")
{
licLimit := middleware.RateLimit(2, 30)
checkoutLimit := middleware.RateLimit(0.5, 10)
webhookLimit := middleware.RateLimit(1, 20)
// License 验证 / 激活 / 停用 / 心跳
register(clientAPI, "POST", "/license/verify", licenseController.Verify)
register(clientAPI, "POST", "/license/activate", licenseController.ActivateDevice)
register(clientAPI, "POST", "/license/deactivate", licenseController.Deactivate)
register(clientAPI, "POST", "/license/heartbeat", licenseController.Heartbeat)
register(clientAPI, "POST", "/license/devices", licenseController.ListDevices)
register(clientAPI, "POST", "/license/verify", licLimit, licenseController.Verify)
register(clientAPI, "POST", "/license/activate", licLimit, licenseController.ActivateDevice)
register(clientAPI, "POST", "/license/deactivate", licLimit, licenseController.Deactivate)
register(clientAPI, "POST", "/license/heartbeat", licLimit, licenseController.Heartbeat)
register(clientAPI, "POST", "/license/devices", licLimit, licenseController.ListDevices)
// 产品定价查询(展示用)
register(clientAPI, "GET", "/products/code/:code", productController.GetByCode)
clientAPI.GET("/products/:product_id/plans", planController.GetByProduct)
// 订单与支付(客户端购买入口 + 支付回调)
register(clientAPI, "POST", "/orders", orderController.CreateOrder)
register(clientAPI, "POST", "/checkout", orderController.Checkout)
register(clientAPI, "POST", "/purchase/claim", orderController.ClaimPurchase)
register(clientAPI, "POST", "/webhooks/payment", orderController.PaymentWebhook)
register(clientAPI, "POST", "/orders", checkoutLimit, orderController.CreateOrder)
register(clientAPI, "POST", "/checkout", checkoutLimit, orderController.Checkout)
register(clientAPI, "POST", "/purchase/claim", checkoutLimit, orderController.ClaimPurchase)
register(clientAPI, "POST", "/webhooks/payment", webhookLimit, orderController.PaymentWebhook)
register(clientAPI, "GET", "/webhooks/waffo", orderController.WaffoWebhookReady)
register(clientAPI, "POST", "/webhooks/waffo", orderController.WaffoWebhook)
register(clientAPI, "POST", "/webhooks/waffo", webhookLimit, orderController.WaffoWebhook)
register(clientAPI, "GET", "/purchase/success", orderController.PurchaseSuccess)
register(clientAPI, "POST", "/purchase/check", orderController.CheckDuplicatePurchase)
register(clientAPI, "POST", "/purchase/status", orderController.CheckPurchaseStatus)
register(clientAPI, "POST", "/purchase/check", checkoutLimit, orderController.CheckDuplicatePurchase)
register(clientAPI, "POST", "/purchase/status", checkoutLimit, orderController.CheckPurchaseStatus)
// 客户权益查询(通过邮箱)
register(clientAPI, "GET", "/entitlements/my", entitlementController.GetByEmail)
register(clientAPI, "GET", "/entitlements/my", checkoutLimit, entitlementController.GetByEmail)
}
// ============ 需要认证的管理路由 ============