增加密码解锁与游客签名 cookie;发帖页对齐 1440 并默认双栏预览;修复 locked 解析、按钮对比度与回复聚焦。 Co-authored-by: Cursor <cursoragent@cursor.com>
58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
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()
|
||
}
|
||
}
|