2025-10-24 03:08:43 +08:00
|
|
|
|
package admin
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"strings"
|
2025-10-26 03:05:27 +08:00
|
|
|
|
|
2026-03-18 21:51:17 +08:00
|
|
|
|
"NetworkAuth/middleware"
|
2026-04-04 20:50:45 +08:00
|
|
|
|
"NetworkAuth/utils"
|
2025-10-24 03:08:43 +08:00
|
|
|
|
|
2026-03-18 21:51:17 +08:00
|
|
|
|
"github.com/gin-gonic/gin"
|
2025-10-24 03:08:43 +08:00
|
|
|
|
"github.com/mojocn/base64Captcha"
|
2026-03-28 23:30:02 +08:00
|
|
|
|
"github.com/sirupsen/logrus"
|
2025-10-24 03:08:43 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// CaptchaHandler 生成验证码图片
|
|
|
|
|
|
// GET /admin/captcha - 返回验证码图片
|
2025-10-26 14:48:02 +08:00
|
|
|
|
func CaptchaHandler(c *gin.Context) {
|
2026-04-04 20:50:45 +08:00
|
|
|
|
// 配置与 User 端一致,采用较弱的验证码强度以提升正常用户体验
|
2025-10-24 03:08:43 +08:00
|
|
|
|
driver := base64Captcha.DriverString{
|
|
|
|
|
|
Height: 60,
|
|
|
|
|
|
Width: 200,
|
2026-04-04 20:50:45 +08:00
|
|
|
|
Length: 4,
|
|
|
|
|
|
NoiseCount: 20, // 加点背景噪点干扰
|
|
|
|
|
|
ShowLineOptions: 2 | 4, // 加点干扰线
|
|
|
|
|
|
Source: "ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789",
|
2025-10-24 03:08:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-04 20:50:45 +08:00
|
|
|
|
// 生成验证码,使用共享的 CaptchaStore
|
|
|
|
|
|
captcha := base64Captcha.NewCaptcha(&driver, utils.CaptchaStore)
|
2025-10-24 03:08:43 +08:00
|
|
|
|
id, b64s, _, err := captcha.Generate()
|
|
|
|
|
|
if err != nil {
|
2026-03-18 21:51:17 +08:00
|
|
|
|
c.String(http.StatusInternalServerError, "生成验证码失败")
|
2025-10-24 03:08:43 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-18 21:51:17 +08:00
|
|
|
|
// 将验证码ID存储到Cookie中
|
|
|
|
|
|
c.SetCookie("captcha_id", id, 300, "/", "", false, true)
|
2025-10-24 03:08:43 +08:00
|
|
|
|
|
2026-03-18 21:51:17 +08:00
|
|
|
|
// 设置响应头
|
2025-10-26 14:48:02 +08:00
|
|
|
|
c.Header("Content-Type", "image/png")
|
|
|
|
|
|
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
|
|
|
|
|
|
c.Header("Pragma", "no-cache")
|
|
|
|
|
|
c.Header("Expires", "0")
|
2025-10-24 03:08:43 +08:00
|
|
|
|
|
|
|
|
|
|
// 去掉data:image/png;base64,前缀
|
|
|
|
|
|
b64s = strings.TrimPrefix(b64s, "data:image/png;base64,")
|
|
|
|
|
|
|
|
|
|
|
|
imgData, err := base64.StdEncoding.DecodeString(b64s)
|
|
|
|
|
|
if err != nil {
|
2026-03-18 21:51:17 +08:00
|
|
|
|
c.String(http.StatusInternalServerError, "解码验证码图片失败")
|
2025-10-24 03:08:43 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-26 14:48:02 +08:00
|
|
|
|
c.Data(http.StatusOK, "image/png", imgData)
|
2025-10-24 03:08:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// VerifyCaptcha 验证验证码
|
|
|
|
|
|
// 这个函数将在登录处理中被调用
|
|
|
|
|
|
// 支持大小写不敏感匹配
|
2025-10-26 14:48:02 +08:00
|
|
|
|
func VerifyCaptcha(c *gin.Context, captchaValue string) bool {
|
2025-10-26 11:57:31 +08:00
|
|
|
|
// 检查是否为开发模式,如果是则跳过验证码验证
|
2025-10-26 22:28:50 +08:00
|
|
|
|
if middleware.ShouldSkipCaptcha(c) {
|
2025-10-26 11:57:31 +08:00
|
|
|
|
return true
|
|
|
|
|
|
}
|
2025-10-27 23:12:15 +08:00
|
|
|
|
|
2025-10-24 03:08:43 +08:00
|
|
|
|
// 从cookie中获取验证码ID
|
2025-10-26 14:48:02 +08:00
|
|
|
|
captchaId, err := c.Cookie("captcha_id")
|
2026-03-18 21:51:17 +08:00
|
|
|
|
if err != nil || captchaId == "" {
|
2026-03-28 23:30:02 +08:00
|
|
|
|
logrus.WithError(err).Warn("验证码验证失败:无法从Cookie获取captcha_id")
|
2025-10-24 03:08:43 +08:00
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-04 20:50:45 +08:00
|
|
|
|
// 调用共享的 VerifyCaptcha
|
|
|
|
|
|
return utils.VerifyCaptcha(captchaId, captchaValue)
|
2025-10-24 03:08:43 +08:00
|
|
|
|
}
|