Files
artalkjs/server/handler/captcha.go
T
2023-03-10 10:39:05 +08:00

155 lines
4.1 KiB
Go

package handler
import (
"bytes"
"strings"
"text/template"
"github.com/ArtalkJS/Artalk/internal/captcha"
"github.com/ArtalkJS/Artalk/internal/config"
"github.com/ArtalkJS/Artalk/internal/i18n"
"github.com/ArtalkJS/Artalk/server/common"
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
)
func Captcha(router fiber.Router) {
ca := router.Group("/captcha/", func(c *fiber.Ctx) error {
if !config.Instance.Captcha.Enabled {
return common.RespError(c, "Captcha disabled")
}
return c.Next()
})
{
ca.Post("/refresh", captchaGet)
ca.Get("/get", captchaGet)
ca.Post("/get", captchaGet)
ca.Post("/check", captchaCheck)
ca.Post("/status", captchaStatus)
}
}
// @Summary Captcha Status
// @Description Get the status of the user's captcha verification
// @Tags Captcha
// @Success 200 {object} common.JSONResult{data=object{is_pass=bool}}
// @Router /captcha/status [post]
func captchaStatus(c *fiber.Ctx) error {
if common.IsReqNeedCaptchaCheck(c) {
return common.RespData(c, common.Map{"is_pass": false})
} else {
return common.RespData(c, common.Map{"is_pass": true})
}
}
// @Summary Captcha Get
// @Description Get a base64 encoded captcha image or a HTML page to verify for user
// @Tags Captcha
// @Success 200 {object} common.JSONResult{data=object{img_data=string}}
// @Router /captcha/refresh [post]
// @Router /captcha/get [get]
// @Router /captcha/get [post]
func captchaGet(c *fiber.Ctx) error {
ip := c.IP()
// ==============
// 图片验证码
// ==============
if config.Instance.Captcha.CaptchaType == config.TypeImage {
return common.RespData(c, common.Map{
"img_data": common.GetNewImageCaptchaBase64(ip),
})
}
// ==============
// iframe 验证码
// ==============
captchaType := config.Instance.Captcha.CaptchaType
ca := captcha.NewCaptcha(captchaType)
iframeHTML, err := captcha.GetIFrameHTML(captchaType)
if err != nil {
return common.RespError(c, "iframe load err: "+err.Error())
}
var buf bytes.Buffer
t := template.New("")
t.Parse(string(iframeHTML))
t.Execute(&buf, ca.PageParams())
// response html body
c.Set(fiber.HeaderContentType, "text/html")
// disable cache
c.Set(fiber.HeaderCacheControl, "no-cache, no-store, must-revalidate")
c.Set(fiber.HeaderPragma, "no-cache")
c.Set(fiber.HeaderExpires, "0")
return c.SendString(buf.String())
}
type ParamsCaptchaCheck struct {
Value string `form:"value" validate:"required"`
}
// @Summary Captcha Check
// @Description Verify user enters correct captcha code
// @Tags Captcha
// @Param value formData string true "the captcha value to check"
// @Success 200 {object} common.JSONResult
// @Failure 400 {object} common.JSONResult{data=object{img_data=string}}
// @Router /captcha/check [post]
func captchaCheck(c *fiber.Ctx) error {
ip := c.IP()
var p ParamsCaptchaCheck
if isOK, resp := common.ParamsDecode(c, &p); !isOK {
return resp
}
inputVal := p.Value
// ===========
// 图片验证码
// ===========
if config.Instance.Captcha.CaptchaType == config.TypeImage {
isPass := strings.ToLower(inputVal) == common.GetImageCaptchaRealCode(ip)
if isPass {
// 验证码正确
common.DisposeImageCaptcha(ip) // 销毁图片验证码
common.OnCaptchaPass(c)
return common.RespSuccess(c)
} else {
// 验证码错误
common.DisposeImageCaptcha(ip)
common.OnCaptchaFail(c)
return common.RespError(c, i18n.T("Wrong captcha"), common.Map{
"img_data": common.GetNewImageCaptchaBase64(ip),
})
}
}
// ==============
// iframe 验证码
// ==============
captchaType := config.Instance.Captcha.CaptchaType
ca := captcha.NewCaptcha(captchaType)
isPass, err := ca.Check(captcha.CaptchaPayload{
CheckValue: p.Value,
UserIP: ip,
})
if isPass {
// 验证成功
common.OnCaptchaPass(c)
return common.RespSuccess(c)
} else {
// 验证失败
common.OnCaptchaFail(c)
logrus.Error("[Captcha] Failed to verify: ", err)
return common.RespError(c, i18n.T("Verification failed"), common.Map{
"detail": err.Error(),
})
}
}