Files
freefire c44b0efa7d feat(hide): 密码可见隐藏块,发帖双栏预览与门禁体验修复
增加密码解锁与游客签名 cookie;发帖页对齐 1440 并默认双栏预览;修复 locked 解析、按钮对比度与回复聚焦。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 05:46:42 +08:00

58 lines
1.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package middleware
import (
"crypto/subtle"
"net/http"
"time"
"github.com/freefire/jiang13-bbs/service"
"github.com/gin-gonic/gin"
)
// EnsureCSRFCookie 若请求尚无 CSRF cookie 则签发一枚(游客也可拿到,供公开写接口双提交)。
// 不改写已有 cookie;生命周期与 refresh 一致(7 天)。
func EnsureCSRFCookie(secure bool) gin.HandlerFunc {
return func(c *gin.Context) {
if raw, err := c.Cookie(service.CSRFCookieName); err != nil || raw == "" {
http.SetCookie(c.Writer, &http.Cookie{
Name: service.CSRFCookieName,
Value: service.GenerateCSRFToken(),
Path: "/",
MaxAge: int((7 * 24 * time.Hour).Seconds()),
HttpOnly: false,
Secure: secure,
SameSite: http.SameSiteLaxMode,
})
}
c.Next()
}
}
// CSRFMiddleware CSRF 防护中间件
// 对 POST/PUT/DELETE 等状态变更请求,校验 X-CSRF-Token header 与 cookie 中的 CSRF token 是否一致
func CSRFMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 仅对状态变更方法校验
if c.Request.Method == http.MethodGet || c.Request.Method == http.MethodHead || c.Request.Method == http.MethodOptions {
c.Next()
return
}
cookieToken, err := c.Cookie(service.CSRFCookieName)
if err != nil || cookieToken == "" {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "CSRF token 缺失"})
return
}
headerToken := c.GetHeader(service.CSRFHeaderName)
// 常量时间比较,避免通过响应耗时逐字节猜测 CSRF token
if headerToken == "" ||
subtle.ConstantTimeCompare([]byte(headerToken), []byte(cookieToken)) != 1 {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "CSRF 校验失败"})
return
}
c.Next()
}
}