go
import (
"crypto/rand"
"encoding/base64"
"github.com/gin-gonic/gin"
)
// 生成 CSRF Token,并将其保存在上下文中
func setCSRFToken() gin.HandlerFunc {
return func(c *gin.Context) {
b := make([]byte, 32)
rand.Read(b)
csrfToken := base64.StdEncoding.EncodeToString(b)
c.Set("csrfToken", csrfToken)
// 有效期为 1 小时
c.SetCookie("csrf_token", csrfToken, 3600, "/", "", false, true)
}
}
// 验证 CSRF Token,注意,在前端只有同时在请求头和cookie中都有才安全
func valiCSRFToken() gin.HandlerFunc {
return func(c *gin.Context) {
// 从请求中获取 CSRF 令牌
csrfToken := c.GetHeader("X-CSRF-Token")
// 从 Cookie 中获取 CSRF 令牌
cookie, err := c.Cookie("csrf_token")
if err != nil {
c.JSON(403, gin.H{"msg": "CSRF令牌无效或过期,请刷新页面重新提交"})
c.Abort()
return
}
// 验证 CSRF 令牌
if csrfToken != cookie {
c.JSON(403, gin.H{"msg": "CSRF 令牌无效"})
c.Abort()
return
}
c.Next()
}
}
// 假设这是路由函数中
// 路由中使用令牌中间件
r.GET("/login",setCSRFToken(), view.Login)
r.POST("/login",valiCSRFToken(), view.Login)
//假设这是get视图函数中
// 读取csrf token
csrfToken, _ := c.Get("csrfToken")
c.HTML(200, "login.html", gin.H{"title": "登陆","csrfToken":csrfToken})
前端模板中使用令牌
js
// 发送 POST 请求
await axios.post('/'+ff, this.form,{
headers: {
'X-CSRF-Token': {{.csrfToken}}
}
})
.then((res) => {
//跳转到首页
window.location.href = '/';
})
.catch(err => {
this.msg = err.response.data.msg;
// 刷新验证码
this.aptchaSrc = "/api/imgcaptcha?" + Math.random();
});